{"thread":{"id":"19365","subject":"[PATCH 0/5] RESEND: git notes","startedAt":"2009-05-16T01:45:03Z","lastAt":"2009-05-20T11:54:53Z","messageCount":19,"participants":["Johan Herland","Junio C Hamano","git@drmicha.warpmail.net","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"114047","messageId":"cover.1242437696.git.johan@herland.net","threadId":"19365","inReplyTo":null,"subject":"[PATCH 0/5] RESEND: git notes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-05-16T01:45:03Z","receivedAt":"2009-05-16T01:45:03Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Hi,\n\nDscho has asked me to take over the responsibility for the js/notes\npatch series.\n\nThe following is a re-roll and resend of the patch series currently\nin pu, plus my own 2 patches for adding support for \"-m\" and \"-F\" to\n\"git notes edit\".\n\nOn advice from Dscho, I have squashed the current bugfix and cleanup\npatches in js/notes into the first 4 \"main\" patches. As a result the\noriginal 15 + 2 patch series is now down to 5 (4 + 1) patches.\n\nIn sum, these 5 patches produce the exact same result as the original\njs/notes series (plus my 2 patches).\n\nI have taken the liberty of squashing the various Signed-off-by tags\n(along with their corresponding patches) into these 5 new patches.\nI hope this is OK with everybody. If not, I apologize; please tell me,\nand I will re-send.\n\n\nHave fun! :)\n\n...Johan\n\n\nJohan Herland (1):\n  Teach \"-m <msg>\" and \"-F <file>\" to \"git notes edit\"\n\nJohannes Schindelin (4):\n  Introduce commit notes\n  Add a script to edit/inspect notes\n  Speed up git notes lookup\n  Add an expensive test for git-notes\n\n .gitignore                       |    1 +\n Documentation/config.txt         |   13 +++\n Documentation/git-notes.txt      |   60 ++++++++++++++\n Makefile                         |    3 +\n cache.h                          |    4 +\n command-list.txt                 |    1 +\n commit.c                         |    1 +\n config.c                         |    5 +\n environment.c                    |    1 +\n git-notes.sh                     |  121 ++++++++++++++++++++++++++++\n notes.c                          |  160 ++++++++++++++++++++++++++++++++++++++\n notes.h                          |    7 ++\n pretty.c                         |    5 +\n t/t3301-notes.sh                 |  149 +++++++++++++++++++++++++++++++++++\n t/t3302-notes-index-expensive.sh |   98 +++++++++++++++++++++++\n 15 files changed, 629 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-notes.txt\n create mode 100755 git-notes.sh\n create mode 100644 notes.c\n create mode 100644 notes.h\n create mode 100755 t/t3301-notes.sh\n create mode 100755 t/t3302-notes-index-expensive.sh\n"},{"id":"114048","messageId":"ef6e0383a970831a3555e6deec8670f13decfed2.1242437696.git.johan@herland.net","threadId":"19365","inReplyTo":"cover.1242437696.git.johan@herland.net","subject":"[PATCH 1/5] Introduce commit notes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-05-16T01:45:04Z","receivedAt":"2009-05-16T01:45:04Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"From: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\nCommit notes are blobs which are shown together with the commit\nmessage.  These blobs are taken from the notes ref, which you can\nconfigure by the config variable core.notesRef, which in turn can\nbe overridden by the environment variable GIT_NOTES_REF.\n\nThe notes ref is a branch which contains \"files\" whose names are\nthe names of the corresponding commits (i.e. the SHA-1).\n\nThe rationale for putting this information into a ref is this: we\nwant to be able to fetch and possibly union-merge the notes,\nmaybe even look at the date when a note was introduced, and we\nwant to store them efficiently together with the other objects.\n\n[tr: fix core.notesRef documentation]\n[tv: fix printing of multi-line notes]\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\nSigned-off-by: Tor Arne Vestbø <tavestbo@trolltech.com>\nSigned-off-by: Johan Herland <johan@herland.net>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config.txt |   13 ++++++++\n Makefile                 |    2 +\n cache.h                  |    4 ++\n commit.c                 |    1 +\n config.c                 |    5 +++\n environment.c            |    1 +\n notes.c                  |   69 ++++++++++++++++++++++++++++++++++++++++++++++\n notes.h                  |    7 ++++\n pretty.c                 |    5 +++\n 9 files changed, 107 insertions(+), 0 deletions(-)\n create mode 100644 notes.c\n create mode 100644 notes.h\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 2c03162..c4c039b 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -438,6 +438,19 @@ On some file system/operating system combinations, this is unreliable.\n Set this config setting to 'rename' there; However, This will remove the\n check that makes sure that existing object files will not get overwritten.\n \n+core.notesRef::\n+\tWhen showing commit messages, also show notes which are stored in\n+\tthe given ref.  This ref is expected to contain files named\n+\tafter the full SHA-1 of the commit they annotate.\n++\n+If such a file exists in the given ref, the referenced blob is read, and\n+appended to the commit message, separated by a \"Notes:\" line.  If the\n+given ref itself does not exist, it is not an error, but means that no\n+notes should be printed.\n++\n+This setting defaults to \"refs/notes/commits\", and can be overridden by\n+the `GIT_NOTES_REF` environment variable.\n+\n alias.*::\n \tCommand aliases for the linkgit:git[1] command wrapper - e.g.\n \tafter defining \"alias.last = cat-file commit HEAD\", the invocation\ndiff --git a/Makefile b/Makefile\nindex a579310..8601bdd 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -409,6 +409,7 @@ LIB_H += ll-merge.h\n LIB_H += log-tree.h\n LIB_H += mailmap.h\n LIB_H += merge-recursive.h\n+LIB_H += notes.h\n LIB_H += object.h\n LIB_H += pack.h\n LIB_H += pack-refs.h\n@@ -492,6 +493,7 @@ LIB_OBJS += match-trees.o\n LIB_OBJS += merge-file.o\n LIB_OBJS += merge-recursive.o\n LIB_OBJS += name-hash.o\n+LIB_OBJS += notes.o\n LIB_OBJS += object.o\n LIB_OBJS += pack-check.o\n LIB_OBJS += pack-refs.o\ndiff --git a/cache.h b/cache.h\nindex b8503ad..8a830a6 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -371,6 +371,8 @@ static inline enum object_type object_type(unsigned int mode)\n #define GITATTRIBUTES_FILE \".gitattributes\"\n #define INFOATTRIBUTES_FILE \"info/attributes\"\n #define ATTRIBUTE_MACRO_PREFIX \"[attr]\"\n+#define GIT_NOTES_REF_ENVIRONMENT \"GIT_NOTES_REF\"\n+#define GIT_NOTES_DEFAULT_REF \"refs/notes/commits\"\n \n extern int is_bare_repository_cfg;\n extern int is_bare_repository(void);\n@@ -561,6 +563,8 @@ enum object_creation_mode {\n \n extern enum object_creation_mode object_creation_mode;\n \n+extern char *notes_ref_name;\n+\n #define GIT_REPO_VERSION 0\n extern int repository_format_version;\n extern int check_repository_format(void);\ndiff --git a/commit.c b/commit.c\nindex aa3b35b..cf72143 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -5,6 +5,7 @@\n #include \"utf8.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n+#include \"notes.h\"\n \n int save_commit_buffer = 1;\n \ndiff --git a/config.c b/config.c\nindex 1682273..d82b580 100644\n--- a/config.c\n+++ b/config.c\n@@ -469,6 +469,11 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.notesref\")) {\n+\t\tnotes_ref_name = xstrdup(value);\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(var, \"core.pager\"))\n \t\treturn git_config_string(&pager_program, var, value);\n \ndiff --git a/environment.c b/environment.c\nindex 801a005..f445cfd 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -50,6 +50,7 @@ enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;\n \n /* Parallel index stat data preload? */\n int core_preload_index = 0;\n+char *notes_ref_name;\n \n /* This is set by setup_git_dir_gently() and/or git_default_config() */\n char *git_work_tree_cfg;\ndiff --git a/notes.c b/notes.c\nnew file mode 100644\nindex 0000000..b0cf553\n--- /dev/null\n+++ b/notes.c\n@@ -0,0 +1,69 @@\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"notes.h\"\n+#include \"refs.h\"\n+#include \"utf8.h\"\n+#include \"strbuf.h\"\n+\n+static int initialized;\n+\n+void get_commit_notes(const struct commit *commit, struct strbuf *sb,\n+\t\tconst char *output_encoding)\n+{\n+\tstatic const char *utf8 = \"utf-8\";\n+\tstruct strbuf name = STRBUF_INIT;\n+\tconst char *hex;\n+\tunsigned char sha1[20];\n+\tchar *msg, *msg_p;\n+\tunsigned long linelen, msglen;\n+\tenum object_type type;\n+\n+\tif (!initialized) {\n+\t\tconst char *env = getenv(GIT_NOTES_REF_ENVIRONMENT);\n+\t\tif (env)\n+\t\t\tnotes_ref_name = getenv(GIT_NOTES_REF_ENVIRONMENT);\n+\t\telse if (!notes_ref_name)\n+\t\t\tnotes_ref_name = GIT_NOTES_DEFAULT_REF;\n+\t\tif (notes_ref_name && read_ref(notes_ref_name, sha1))\n+\t\t\tnotes_ref_name = NULL;\n+\t\tinitialized = 1;\n+\t}\n+\n+\tif (!notes_ref_name)\n+\t\treturn;\n+\n+\tstrbuf_addf(&name, \"%s:%s\", notes_ref_name,\n+\t\t\tsha1_to_hex(commit->object.sha1));\n+\tif (get_sha1(name.buf, sha1))\n+\t\treturn;\n+\n+\tif (!(msg = read_sha1_file(sha1, &type, &msglen)) || !msglen ||\n+\t\t\ttype != OBJ_BLOB)\n+\t\treturn;\n+\n+\tif (output_encoding && *output_encoding &&\n+\t\t\tstrcmp(utf8, output_encoding)) {\n+\t\tchar *reencoded = reencode_string(msg, output_encoding, utf8);\n+\t\tif (reencoded) {\n+\t\t\tfree(msg);\n+\t\t\tmsg = reencoded;\n+\t\t\tmsglen = strlen(msg);\n+\t\t}\n+\t}\n+\n+\t/* we will end the annotation by a newline anyway */\n+\tif (msglen && msg[msglen - 1] == '\\n')\n+\t\tmsglen--;\n+\n+\tstrbuf_addstr(sb, \"\\nNotes:\\n\");\n+\n+\tfor (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {\n+\t\tlinelen = strchrnul(msg_p, '\\n') - msg_p;\n+\n+\t\tstrbuf_addstr(sb, \"    \");\n+\t\tstrbuf_add(sb, msg_p, linelen);\n+\t\tstrbuf_addch(sb, '\\n');\n+\t}\n+\n+\tfree(msg);\n+}\ndiff --git a/notes.h b/notes.h\nnew file mode 100644\nindex 0000000..79d21b6\n--- /dev/null\n+++ b/notes.h\n@@ -0,0 +1,7 @@\n+#ifndef NOTES_H\n+#define NOTES_H\n+\n+void get_commit_notes(const struct commit *commit, struct strbuf *sb,\n+\t\tconst char *output_encoding);\n+\n+#endif\ndiff --git a/pretty.c b/pretty.c\nindex a0ef356..d30a964 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -6,6 +6,7 @@\n #include \"string-list.h\"\n #include \"mailmap.h\"\n #include \"log-tree.h\"\n+#include \"notes.h\"\n #include \"color.h\"\n \n static char *user_format;\n@@ -963,5 +964,9 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,\n \t */\n \tif (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)\n \t\tstrbuf_addch(sb, '\\n');\n+\n+\tif (fmt != CMIT_FMT_ONELINE)\n+\t\tget_commit_notes(commit, sb, encoding);\n+\n \tfree(reencoded);\n }\n-- \n1.6.3.rc0.1.gf800\n"},{"id":"114049","messageId":"2d84619e4a4b0bc85fd310aa1799633fd9b62399.1242437696.git.johan@herland.net","threadId":"19365","inReplyTo":"cover.1242437696.git.johan@herland.net","subject":"[PATCH 2/5] Add a script to edit/inspect notes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-05-16T01:45:05Z","receivedAt":"2009-05-16T01:45:05Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"From: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\nThe script 'git notes' allows you to edit and show commit notes, by\ncalling either\n\n\tgit notes show <commit>\n\nor\n\n\tgit notes edit <commit>\n\n[tv: fix printing of multi-line notes]\n[mg: test and handle empty notes gracefully]\n[tr: only clean up message file when editing]\n[tr: use GIT_EDITOR and core.editor over VISUAL/EDITOR]\n[tr: t3301: fix confusing quoting in test for valid notes ref]\n[tr: t3301: use test_must_fail instead of !]\n[tr: refuse to edit notes outside refs/notes/]\n[jc: tests: fix \"export var=val\"]\n[cc: Documentation: fix 'linkgit' macro in \"git-notes.txt\"]\n[jh: Minor cleanup and bugfixing in git-notes.sh]\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Tor Arne Vestbø <tavestbo@trolltech.com>\nSigned-off-by: Michael J Gruber <git@drmicha.warpmail.net>\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Johan Herland <johan@herland.net>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n .gitignore                  |    1 +\n Documentation/git-notes.txt |   46 +++++++++++++++++\n Makefile                    |    1 +\n command-list.txt            |    1 +\n git-notes.sh                |   73 +++++++++++++++++++++++++++\n t/t3301-notes.sh            |  114 +++++++++++++++++++++++++++++++++++++++++++\n 6 files changed, 236 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-notes.txt\n create mode 100755 git-notes.sh\n create mode 100755 t/t3301-notes.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex 41c0b20..13221e7 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -86,6 +86,7 @@ git-mktag\n git-mktree\n git-name-rev\n git-mv\n+git-notes\n git-pack-redundant\n git-pack-objects\n git-pack-refs\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nnew file mode 100644\nindex 0000000..7136016\n--- /dev/null\n+++ b/Documentation/git-notes.txt\n@@ -0,0 +1,46 @@\n+git-notes(1)\n+============\n+\n+NAME\n+----\n+git-notes - Add/inspect commit notes\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git-notes' (edit | show) [commit]\n+\n+DESCRIPTION\n+-----------\n+This command allows you to add notes to commit messages, without\n+changing the commit.  To discern these notes from the message stored\n+in the commit object, the notes are indented like the message, after\n+an unindented line saying \"Notes:\".\n+\n+To disable commit notes, you have to set the config variable\n+core.notesRef to the empty string.  Alternatively, you can set it\n+to a different ref, something like \"refs/notes/bugzilla\".  This setting\n+can be overridden by the environment variable \"GIT_NOTES_REF\".\n+\n+\n+SUBCOMMANDS\n+-----------\n+\n+edit::\n+\tEdit the notes for a given commit (defaults to HEAD).\n+\n+show::\n+\tShow the notes for a given commit (defaults to HEAD).\n+\n+\n+Author\n+------\n+Written by Johannes Schindelin <johannes.schindelin@gmx.de>\n+\n+Documentation\n+-------------\n+Documentation by Johannes Schindelin\n+\n+GIT\n+---\n+Part of the linkgit:git[7] suite\ndiff --git a/Makefile b/Makefile\nindex 8601bdd..ab4a2b7 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -298,6 +298,7 @@ SCRIPT_SH += git-merge-one-file.sh\n SCRIPT_SH += git-merge-resolve.sh\n SCRIPT_SH += git-mergetool.sh\n SCRIPT_SH += git-mergetool--lib.sh\n+SCRIPT_SH += git-notes.sh\n SCRIPT_SH += git-parse-remote.sh\n SCRIPT_SH += git-pull.sh\n SCRIPT_SH += git-quiltimport.sh\ndiff --git a/command-list.txt b/command-list.txt\nindex fb03a2e..4296941 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -74,6 +74,7 @@ git-mktag                               plumbingmanipulators\n git-mktree                              plumbingmanipulators\n git-mv                                  mainporcelain common\n git-name-rev                            plumbinginterrogators\n+git-notes                               mainporcelain\n git-pack-objects                        plumbingmanipulators\n git-pack-redundant                      plumbinginterrogators\n git-pack-refs                           ancillarymanipulators\ndiff --git a/git-notes.sh b/git-notes.sh\nnew file mode 100755\nindex 0000000..7c3b8b9\n--- /dev/null\n+++ b/git-notes.sh\n@@ -0,0 +1,73 @@\n+#!/bin/sh\n+\n+USAGE=\"(edit | show) [commit]\"\n+. git-sh-setup\n+\n+test -n \"$3\" && usage\n+\n+test -z \"$1\" && usage\n+ACTION=\"$1\"; shift\n+\n+test -z \"$GIT_NOTES_REF\" && GIT_NOTES_REF=\"$(git config core.notesref)\"\n+test -z \"$GIT_NOTES_REF\" && GIT_NOTES_REF=\"refs/notes/commits\"\n+\n+COMMIT=$(git rev-parse --verify --default HEAD \"$@\") ||\n+die \"Invalid commit: $@\"\n+\n+case \"$ACTION\" in\n+edit)\n+\tif [ \"${GIT_NOTES_REF#refs/notes/}\" = \"$GIT_NOTES_REF\" ]; then\n+\t\tdie \"Refusing to edit notes in $GIT_NOTES_REF (outside of refs/notes/)\"\n+\tfi\n+\n+\tMSG_FILE=\"$GIT_DIR/new-notes-$COMMIT\"\n+\tGIT_INDEX_FILE=\"MSG_FILE.idx\"\n+\texport GIT_INDEX_FILE\n+\n+\ttrap '\n+\t\ttest -f \"$MSG_FILE\" && rm \"$MSG_FILE\"\n+\t\ttest -f \"$GIT_INDEX_FILE\" && rm \"$GIT_INDEX_FILE\"\n+\t' 0\n+\n+\tgit log -1 $COMMIT | sed \"s/^/#/\" > \"$MSG_FILE\"\n+\n+\tCURRENT_HEAD=$(git show-ref \"$GIT_NOTES_REF\" | cut -f 1 -d ' ')\n+\tif [ -z \"$CURRENT_HEAD\" ]; then\n+\t\tPARENT=\n+\telse\n+\t\tPARENT=\"-p $CURRENT_HEAD\"\n+\t\tgit read-tree \"$GIT_NOTES_REF\" || die \"Could not read index\"\n+\t\tgit cat-file blob :$COMMIT >> \"$MSG_FILE\" 2> /dev/null\n+\tfi\n+\n+\tcore_editor=\"$(git config core.editor)\"\n+\t${GIT_EDITOR:-${core_editor:-${VISUAL:-${EDITOR:-vi}}}} \"$MSG_FILE\"\n+\n+\tgrep -v ^# < \"$MSG_FILE\" | git stripspace > \"$MSG_FILE\".processed\n+\tmv \"$MSG_FILE\".processed \"$MSG_FILE\"\n+\tif [ -s \"$MSG_FILE\" ]; then\n+\t\tBLOB=$(git hash-object -w \"$MSG_FILE\") ||\n+\t\t\tdie \"Could not write into object database\"\n+\t\tgit update-index --add --cacheinfo 0644 $BLOB $COMMIT ||\n+\t\t\tdie \"Could not write index\"\n+\telse\n+\t\ttest -z \"$CURRENT_HEAD\" &&\n+\t\t\tdie \"Will not initialise with empty tree\"\n+\t\tgit update-index --force-remove $COMMIT ||\n+\t\t\tdie \"Could not update index\"\n+\tfi\n+\n+\tTREE=$(git write-tree) || die \"Could not write tree\"\n+\tNEW_HEAD=$(echo Annotate $COMMIT | git commit-tree $TREE $PARENT) ||\n+\t\tdie \"Could not annotate\"\n+\tgit update-ref -m \"Annotate $COMMIT\" \\\n+\t\t\"$GIT_NOTES_REF\" $NEW_HEAD $CURRENT_HEAD\n+;;\n+show)\n+\tgit rev-parse -q --verify \"$GIT_NOTES_REF\":$COMMIT > /dev/null ||\n+\t\tdie \"No note for commit $COMMIT.\"\n+\tgit show \"$GIT_NOTES_REF\":$COMMIT\n+;;\n+*)\n+\tusage\n+esac\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nnew file mode 100755\nindex 0000000..73e53be\n--- /dev/null\n+++ b/t/t3301-notes.sh\n@@ -0,0 +1,114 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2007 Johannes E. Schindelin\n+#\n+\n+test_description='Test commit notes'\n+\n+. ./test-lib.sh\n+\n+cat > fake_editor.sh << \\EOF\n+echo \"$MSG\" > \"$1\"\n+echo \"$MSG\" >& 2\n+EOF\n+chmod a+x fake_editor.sh\n+VISUAL=./fake_editor.sh\n+export VISUAL\n+\n+test_expect_success 'cannot annotate non-existing HEAD' '\n+\t(MSG=3 && export MSG && test_must_fail git notes edit)\n+'\n+\n+test_expect_success setup '\n+\t: > a1 &&\n+\tgit add a1 &&\n+\ttest_tick &&\n+\tgit commit -m 1st &&\n+\t: > a2 &&\n+\tgit add a2 &&\n+\ttest_tick &&\n+\tgit commit -m 2nd\n+'\n+\n+test_expect_success 'need valid notes ref' '\n+\t(MSG=1 GIT_NOTES_REF=/ && export MSG GIT_NOTES_REF &&\n+\t test_must_fail git notes edit) &&\n+\t(MSG=2 GIT_NOTES_REF=/ && export MSG GIT_NOTES_REF &&\n+\t test_must_fail git notes show)\n+'\n+\n+test_expect_success 'refusing to edit in refs/heads/' '\n+\t(MSG=1 GIT_NOTES_REF=refs/heads/bogus &&\n+\t export MSG GIT_NOTES_REF &&\n+\t test_must_fail git notes edit)\n+'\n+\n+test_expect_success 'refusing to edit in refs/remotes/' '\n+\t(MSG=1 GIT_NOTES_REF=refs/remotes/bogus &&\n+\t export MSG GIT_NOTES_REF &&\n+\t test_must_fail git notes edit)\n+'\n+\n+# 1 indicates caught gracefully by die, 128 means git-show barked\n+test_expect_success 'handle empty notes gracefully' '\n+\tgit notes show ; test 1 = $?\n+'\n+\n+test_expect_success 'create notes' '\n+\tgit config core.notesRef refs/notes/commits &&\n+\tMSG=b1 git notes edit &&\n+\ttest ! -f .git/new-notes &&\n+\ttest 1 = $(git ls-tree refs/notes/commits | wc -l) &&\n+\ttest b1 = $(git notes show) &&\n+\tgit show HEAD^ &&\n+\ttest_must_fail git notes show HEAD^\n+'\n+\n+cat > expect << EOF\n+commit 268048bfb8a1fb38e703baceb8ab235421bf80c5\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:14:13 2005 -0700\n+\n+    2nd\n+\n+Notes:\n+    b1\n+EOF\n+\n+test_expect_success 'show notes' '\n+\t! (git cat-file commit HEAD | grep b1) &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+test_expect_success 'create multi-line notes (setup)' '\n+\t: > a3 &&\n+\tgit add a3 &&\n+\ttest_tick &&\n+\tgit commit -m 3rd &&\n+\tMSG=\"b3\n+c3c3c3c3\n+d3d3d3\" git notes edit\n+'\n+\n+cat > expect-multiline << EOF\n+commit 1584215f1d29c65e99c6c6848626553fdd07fd75\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:15:13 2005 -0700\n+\n+    3rd\n+\n+Notes:\n+    b3\n+    c3c3c3c3\n+    d3d3d3\n+EOF\n+\n+printf \"\\n\" >> expect-multiline\n+cat expect >> expect-multiline\n+\n+test_expect_success 'show multi-line notes' '\n+\tgit log -2 > output &&\n+\ttest_cmp expect-multiline output\n+'\n+\n+test_done\n-- \n1.6.3.rc0.1.gf800\n"},{"id":"114052","messageId":"03f4b437b69cc92e049b6df54858dcb7d6911c86.1242437696.git.johan@herland.net","threadId":"19365","inReplyTo":"cover.1242437696.git.johan@herland.net","subject":"[PATCH 3/5] Speed up git notes lookup","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-05-16T01:45:06Z","receivedAt":"2009-05-16T01:45:06Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"From: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\nTo avoid looking up each and every commit in the notes ref's tree\nobject, which is very expensive, speed things up by slurping the tree\nobject's contents into a hash_map.\n\nThe idea for the hashmap singleton is from David Reiss, initial\nbenchmarking by Jeff King.\n\nNote: the implementation allows for arbitrary entries in the notes\ntree object, ignoring those that do not reference a valid object.  This\nallows you to annotate arbitrary branches, or objects.\n\n[jc: fixed an obvious error in initialize_hash_map()]\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Johan Herland <johan@herland.net>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n notes.c |  113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------\n 1 files changed, 102 insertions(+), 11 deletions(-)\n\ndiff --git a/notes.c b/notes.c\nindex b0cf553..bd73784 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -4,16 +4,112 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n #include \"strbuf.h\"\n+#include \"tree-walk.h\"\n+\n+struct entry {\n+\tunsigned char commit_sha1[20];\n+\tunsigned char notes_sha1[20];\n+};\n+\n+struct hash_map {\n+\tstruct entry *entries;\n+\toff_t count, size;\n+};\n \n static int initialized;\n+static struct hash_map hash_map;\n+\n+static int hash_index(struct hash_map *map, const unsigned char *sha1)\n+{\n+\tint i = ((*(unsigned int *)sha1) % map->size);\n+\n+\tfor (;;) {\n+\t\tunsigned char *current = map->entries[i].commit_sha1;\n+\n+\t\tif (!hashcmp(sha1, current))\n+\t\t\treturn i;\n+\n+\t\tif (is_null_sha1(current))\n+\t\t\treturn -1 - i;\n+\n+\t\tif (++i == map->size)\n+\t\t\ti = 0;\n+\t}\n+}\n+\n+static void add_entry(const unsigned char *commit_sha1,\n+\t\tconst unsigned char *notes_sha1)\n+{\n+\tint index;\n+\n+\tif (hash_map.count + 1 > hash_map.size >> 1) {\n+\t\tint i, old_size = hash_map.size;\n+\t\tstruct entry *old = hash_map.entries;\n+\n+\t\thash_map.size = old_size ? old_size << 1 : 64;\n+\t\thash_map.entries = (struct entry *)\n+\t\t\txcalloc(sizeof(struct entry), hash_map.size);\n+\n+\t\tfor (i = 0; i < old_size; i++)\n+\t\t\tif (!is_null_sha1(old[i].commit_sha1)) {\n+\t\t\t\tindex = -1 - hash_index(&hash_map,\n+\t\t\t\t\t\told[i].commit_sha1);\n+\t\t\t\tmemcpy(hash_map.entries + index, old + i,\n+\t\t\t\t\tsizeof(struct entry));\n+\t\t\t}\n+\t\tfree(old);\n+\t}\n+\n+\tindex = hash_index(&hash_map, commit_sha1);\n+\tif (index < 0) {\n+\t\tindex = -1 - index;\n+\t\thash_map.count++;\n+\t}\n+\n+\thashcpy(hash_map.entries[index].commit_sha1, commit_sha1);\n+\thashcpy(hash_map.entries[index].notes_sha1, notes_sha1);\n+}\n+\n+static void initialize_hash_map(const char *notes_ref_name)\n+{\n+\tunsigned char sha1[20], commit_sha1[20];\n+\tunsigned mode;\n+\tstruct tree_desc desc;\n+\tstruct name_entry entry;\n+\tvoid *buf;\n+\n+\tif (!notes_ref_name || read_ref(notes_ref_name, commit_sha1) ||\n+\t    get_tree_entry(commit_sha1, \"\", sha1, &mode))\n+\t\treturn;\n+\n+\tbuf = fill_tree_descriptor(&desc, sha1);\n+\tif (!buf)\n+\t\tdie(\"Could not read %s for notes-index\", sha1_to_hex(sha1));\n+\n+\twhile (tree_entry(&desc, &entry))\n+\t\tif (!get_sha1(entry.path, commit_sha1))\n+\t\t\tadd_entry(commit_sha1, entry.sha1);\n+\tfree(buf);\n+}\n+\n+static unsigned char *lookup_notes(const unsigned char *commit_sha1)\n+{\n+\tint index;\n+\n+\tif (!hash_map.size)\n+\t\treturn NULL;\n+\n+\tindex = hash_index(&hash_map, commit_sha1);\n+\tif (index < 0)\n+\t\treturn NULL;\n+\treturn hash_map.entries[index].notes_sha1;\n+}\n \n void get_commit_notes(const struct commit *commit, struct strbuf *sb,\n \t\tconst char *output_encoding)\n {\n \tstatic const char *utf8 = \"utf-8\";\n-\tstruct strbuf name = STRBUF_INIT;\n-\tconst char *hex;\n-\tunsigned char sha1[20];\n+\tunsigned char *sha1;\n \tchar *msg, *msg_p;\n \tunsigned long linelen, msglen;\n \tenum object_type type;\n@@ -24,17 +120,12 @@ void get_commit_notes(const struct commit *commit, struct strbuf *sb,\n \t\t\tnotes_ref_name = getenv(GIT_NOTES_REF_ENVIRONMENT);\n \t\telse if (!notes_ref_name)\n \t\t\tnotes_ref_name = GIT_NOTES_DEFAULT_REF;\n-\t\tif (notes_ref_name && read_ref(notes_ref_name, sha1))\n-\t\t\tnotes_ref_name = NULL;\n+\t\tinitialize_hash_map(notes_ref_name);\n \t\tinitialized = 1;\n \t}\n \n-\tif (!notes_ref_name)\n-\t\treturn;\n-\n-\tstrbuf_addf(&name, \"%s:%s\", notes_ref_name,\n-\t\t\tsha1_to_hex(commit->object.sha1));\n-\tif (get_sha1(name.buf, sha1))\n+\tsha1 = lookup_notes(commit->object.sha1);\n+\tif (!sha1)\n \t\treturn;\n \n \tif (!(msg = read_sha1_file(sha1, &type, &msglen)) || !msglen ||\n-- \n1.6.3.rc0.1.gf800\n"},{"id":"114051","messageId":"d6dcde288fd1d6c568adf06d804e85757d5d06e8.1242437696.git.johan@herland.net","threadId":"19365","inReplyTo":"cover.1242437696.git.johan@herland.net","subject":"[PATCH 4/5] Add an expensive test for git-notes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-05-16T01:45:07Z","receivedAt":"2009-05-16T01:45:07Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"From: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\ngit-notes have the potential of being pretty expensive, so test with\na lot of commits.  A lot.  So to make things cheaper, you have to\nopt-in explicitely, by setting the environment variable\nGIT_NOTES_TIMING_TESTS.\n\n[jc: tests: fix \"export var=val\"]\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Johan Herland <johan@herland.net>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n t/t3302-notes-index-expensive.sh |   98 ++++++++++++++++++++++++++++++++++++++\n 1 files changed, 98 insertions(+), 0 deletions(-)\n create mode 100755 t/t3302-notes-index-expensive.sh\n\ndiff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh\nnew file mode 100755\nindex 0000000..0ef3e95\n--- /dev/null\n+++ b/t/t3302-notes-index-expensive.sh\n@@ -0,0 +1,98 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2007 Johannes E. Schindelin\n+#\n+\n+test_description='Test commit notes index (expensive!)'\n+\n+. ./test-lib.sh\n+\n+test -z \"$GIT_NOTES_TIMING_TESTS\" && {\n+\tsay Skipping timing tests\n+\ttest_done\n+\texit\n+}\n+\n+create_repo () {\n+\tnumber_of_commits=$1\n+\tnr=0\n+\tparent=\n+\ttest -d .git || {\n+\tgit init &&\n+\ttree=$(git write-tree) &&\n+\twhile [ $nr -lt $number_of_commits ]; do\n+\t\ttest_tick &&\n+\t\tcommit=$(echo $nr | git commit-tree $tree $parent) ||\n+\t\t\treturn\n+\t\tparent=\"-p $commit\"\n+\t\tnr=$(($nr+1))\n+\tdone &&\n+\tgit update-ref refs/heads/master $commit &&\n+\t{\n+\t\tGIT_INDEX_FILE=.git/temp; export GIT_INDEX_FILE;\n+\t\tgit rev-list HEAD | cat -n | sed \"s/^[ \t][ \t]*/ /g\" |\n+\t\twhile read nr sha1; do\n+\t\t\tblob=$(echo note $nr | git hash-object -w --stdin) &&\n+\t\t\techo $sha1 | sed \"s/^/0644 $blob 0\t/\"\n+\t\tdone | git update-index --index-info &&\n+\t\ttree=$(git write-tree) &&\n+\t\ttest_tick &&\n+\t\tcommit=$(echo notes | git commit-tree $tree) &&\n+\t\tgit update-ref refs/notes/commits $commit\n+\t} &&\n+\tgit config core.notesRef refs/notes/commits\n+\t}\n+}\n+\n+test_notes () {\n+\tcount=$1 &&\n+\tgit config core.notesRef refs/notes/commits &&\n+\tgit log | grep \"^    \" > output &&\n+\ti=1 &&\n+\twhile [ $i -le $count ]; do\n+\t\techo \"    $(($count-$i))\" &&\n+\t\techo \"    note $i\" &&\n+\t\ti=$(($i+1));\n+\tdone > expect &&\n+\tgit diff expect output\n+}\n+\n+cat > time_notes << \\EOF\n+\tmode=$1\n+\ti=1\n+\twhile [ $i -lt $2 ]; do\n+\t\tcase $1 in\n+\t\tno-notes)\n+\t\t\tGIT_NOTES_REF=non-existing; export GIT_NOTES_REF\n+\t\t;;\n+\t\tnotes)\n+\t\t\tunset GIT_NOTES_REF\n+\t\t;;\n+\t\tesac\n+\t\tgit log >/dev/null\n+\t\ti=$(($i+1))\n+\tdone\n+EOF\n+\n+time_notes () {\n+\tfor mode in no-notes notes\n+\tdo\n+\t\techo $mode\n+\t\t/usr/bin/time sh ../time_notes $mode $1\n+\tdone\n+}\n+\n+for count in 10 100 1000 10000; do\n+\n+\tmkdir $count\n+\t(cd $count;\n+\n+\ttest_expect_success \"setup $count\" \"create_repo $count\"\n+\n+\ttest_expect_success 'notes work' \"test_notes $count\"\n+\n+\ttest_expect_success 'notes timing' \"time_notes 100\"\n+\t)\n+done\n+\n+test_done\n-- \n1.6.3.rc0.1.gf800\n"},{"id":"114050","messageId":"497f95c7fbca7f757570dfdda9aab00ae2d427e3.1242437696.git.johan@herland.net","threadId":"19365","inReplyTo":"cover.1242437696.git.johan@herland.net","subject":"[PATCH 5/5] Teach \"-m <msg>\" and \"-F <file>\" to \"git notes edit\"","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-05-16T01:45:08Z","receivedAt":"2009-05-16T01:45:08Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"The \"-m\" and \"-F\" options are already the established method\n(in both git-commit and git-tag) to specify a commit/tag message\nwithout invoking the editor. This patch teaches \"git notes edit\"\nto respect the same options for specifying a notes message without\ninvoking the editor.\n\nMultiple \"-m\" and/or \"-F\" options are concatenated as separate\nparagraphs.\n\nThe patch also updates the \"git notes\" documentation and adds\nselftests for the new functionality. Unfortunately, the added\nselftests include a couple of lines with trailing whitespace\n(without these the test will fail). This may cause git to warn\nabout \"whitespace errors\".\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/git-notes.txt |   16 ++++++++++-\n git-notes.sh                |   64 +++++++++++++++++++++++++++++++++++++-----\n t/t3301-notes.sh            |   35 +++++++++++++++++++++++\n 3 files changed, 106 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex 7136016..94cceb1 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -8,7 +8,7 @@ git-notes - Add/inspect commit notes\n SYNOPSIS\n --------\n [verse]\n-'git-notes' (edit | show) [commit]\n+'git-notes' (edit [-F <file> | -m <msg>] | show) [commit]\n \n DESCRIPTION\n -----------\n@@ -33,6 +33,20 @@ show::\n \tShow the notes for a given commit (defaults to HEAD).\n \n \n+OPTIONS\n+-------\n+-m <msg>::\n+\tUse the given note message (instead of prompting).\n+\tIf multiple `-m` (or `-F`) options are given, their\n+\tvalues are concatenated as separate paragraphs.\n+\n+-F <file>::\n+\tTake the note message from the given file.  Use '-' to\n+\tread the note message from the standard input.\n+\tIf multiple `-F` (or `-m`) options are given, their\n+\tvalues are concatenated as separate paragraphs.\n+\n+\n Author\n ------\n Written by Johannes Schindelin <johannes.schindelin@gmx.de>\ndiff --git a/git-notes.sh b/git-notes.sh\nindex 7c3b8b9..0119f1f 100755\n--- a/git-notes.sh\n+++ b/git-notes.sh\n@@ -1,16 +1,59 @@\n #!/bin/sh\n \n-USAGE=\"(edit | show) [commit]\"\n+USAGE=\"(edit [-F <file> | -m <msg>] | show) [commit]\"\n . git-sh-setup\n \n-test -n \"$3\" && usage\n-\n test -z \"$1\" && usage\n ACTION=\"$1\"; shift\n \n test -z \"$GIT_NOTES_REF\" && GIT_NOTES_REF=\"$(git config core.notesref)\"\n test -z \"$GIT_NOTES_REF\" && GIT_NOTES_REF=\"refs/notes/commits\"\n \n+MESSAGE=\n+while test $# != 0\n+do\n+\tcase \"$1\" in\n+\t-m)\n+\t\ttest \"$ACTION\" = \"edit\" || usage\n+\t\tshift\n+\t\tif test \"$#\" = \"0\"; then\n+\t\t\tdie \"error: option -m needs an argument\"\n+\t\telse\n+\t\t\tif [ -z \"$MESSAGE\" ]; then\n+\t\t\t\tMESSAGE=\"$1\"\n+\t\t\telse\n+\t\t\t\tMESSAGE=\"$MESSAGE\n+\n+$1\"\n+\t\t\tfi\n+\t\t\tshift\n+\t\tfi\n+\t\t;;\n+\t-F)\n+\t\ttest \"$ACTION\" = \"edit\" || usage\n+\t\tshift\n+\t\tif test \"$#\" = \"0\"; then\n+\t\t\tdie \"error: option -F needs an argument\"\n+\t\telse\n+\t\t\tif [ -z \"$MESSAGE\" ]; then\n+\t\t\t\tMESSAGE=\"$(cat \"$1\")\"\n+\t\t\telse\n+\t\t\t\tMESSAGE=\"$MESSAGE\n+\n+$(cat \"$1\")\"\n+\t\t\tfi\n+\t\t\tshift\n+\t\tfi\n+\t\t;;\n+\t-*)\n+\t\tusage\n+\t\t;;\n+\t*)\n+\t\tbreak\n+\t\t;;\n+\tesac\n+done\n+\n COMMIT=$(git rev-parse --verify --default HEAD \"$@\") ||\n die \"Invalid commit: $@\"\n \n@@ -29,19 +72,24 @@ edit)\n \t\ttest -f \"$GIT_INDEX_FILE\" && rm \"$GIT_INDEX_FILE\"\n \t' 0\n \n-\tgit log -1 $COMMIT | sed \"s/^/#/\" > \"$MSG_FILE\"\n-\n \tCURRENT_HEAD=$(git show-ref \"$GIT_NOTES_REF\" | cut -f 1 -d ' ')\n \tif [ -z \"$CURRENT_HEAD\" ]; then\n \t\tPARENT=\n \telse\n \t\tPARENT=\"-p $CURRENT_HEAD\"\n \t\tgit read-tree \"$GIT_NOTES_REF\" || die \"Could not read index\"\n-\t\tgit cat-file blob :$COMMIT >> \"$MSG_FILE\" 2> /dev/null\n \tfi\n \n-\tcore_editor=\"$(git config core.editor)\"\n-\t${GIT_EDITOR:-${core_editor:-${VISUAL:-${EDITOR:-vi}}}} \"$MSG_FILE\"\n+\tif [ -z \"$MESSAGE\" ]; then\n+\t\tgit log -1 $COMMIT | sed \"s/^/#/\" > \"$MSG_FILE\"\n+\t\tif [ ! -z \"$CURRENT_HEAD\" ]; then\n+\t\t\tgit cat-file blob :$COMMIT >> \"$MSG_FILE\" 2> /dev/null\n+\t\tfi\n+\t\tcore_editor=\"$(git config core.editor)\"\n+\t\t${GIT_EDITOR:-${core_editor:-${VISUAL:-${EDITOR:-vi}}}} \"$MSG_FILE\"\n+\telse\n+\t\techo \"$MESSAGE\" > \"$MSG_FILE\"\n+\tfi\n \n \tgrep -v ^# < \"$MSG_FILE\" | git stripspace > \"$MSG_FILE\".processed\n \tmv \"$MSG_FILE\".processed \"$MSG_FILE\"\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex 73e53be..9eaa338 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -110,5 +110,40 @@ test_expect_success 'show multi-line notes' '\n \tgit log -2 > output &&\n \ttest_cmp expect-multiline output\n '\n+test_expect_success 'create -m and -F notes (setup)' '\n+\t: > a4 &&\n+\tgit add a4 &&\n+\ttest_tick &&\n+\tgit commit -m 4th &&\n+\techo \"xyzzy\" > note5 &&\n+\tgit notes edit -m spam -F note5 -m \"foo\n+bar\n+baz\"\n+'\n+\n+cat > expect-m-and-F << EOF\n+commit 15023535574ded8b1a89052b32673f84cf9582b8\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:16:13 2005 -0700\n+\n+    4th\n+\n+Notes:\n+    spam\n+    \n+    xyzzy\n+    \n+    foo\n+    bar\n+    baz\n+EOF\n+\n+printf \"\\n\" >> expect-m-and-F\n+cat expect-multiline >> expect-m-and-F\n+\n+test_expect_success 'show -m and -F notes' '\n+\tgit log -3 > output &&\n+\ttest_cmp expect-m-and-F output\n+'\n \n test_done\n-- \n1.6.3.rc0.1.gf800\n"},{"id":"114076","messageId":"7vpre9plwl.fsf@alter.siamese.dyndns.org","threadId":"19365","inReplyTo":"cover.1242437696.git.johan@herland.net","subject":"Re: [PATCH 0/5] RESEND: git notes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-16T07:06:02Z","receivedAt":"2009-05-16T07:06:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> The following is a re-roll and resend of the patch series currently\n> in pu, plus my own 2 patches for adding support for \"-m\" and \"-F\" to\n> \"git notes edit\".\n>\n> On advice from Dscho, I have squashed the current bugfix and cleanup\n> patches in js/notes into the first 4 \"main\" patches. As a result the\n> original 15 + 2 patch series is now down to 5 (4 + 1) patches.\n>\n> In sum, these 5 patches produce the exact same result as the original\n> js/notes series (plus my 2 patches).\n\nThanks, but you did no such thing, actually.\n\nThe result from this sequence\n\n\tgit checkout -b jh/notes v1.6.3.1^0\n        git am ./+jh-notes.mbox ;# these patches\n\tgit checkout v1.6.3.1^0\n        git merge js/notes ;# allow rerere to reapply the resolution\n        git diff -R jh/notes^\n\nshould be empty, or should show your improvements over what has been\nqueued in 'pu'.\n\nHere is what I see:\n\n    diff --git b/git-notes.sh a/git-notes.sh\n    index 6ec33c9..7c3b8b9 100755\n    --- b/git-notes.sh\n    +++ a/git-notes.sh\n    @@ -20,15 +20,16 @@ edit)\n                    die \"Refusing to edit notes in $GIT_NOTES_REF (outside of refs/notes/)\"\n            fi\n\n    -\tMESSAGE=\"$GIT_DIR\"/new-notes-$COMMIT\n    +\tMSG_FILE=\"$GIT_DIR/new-notes-$COMMIT\"\n    +\tGIT_INDEX_FILE=\"MSG_FILE.idx\"\n\nRenaming of the variable MESSAGE to MSG_FILE may be an improvement, as the\nformer could have puzzled the reader if $MESSAGE contains the message\nitself, or it has the name of a file that stores the message (the answer\nis latter).\n\nBut I think there is a regression with a missing '$' here.  \n\n    +\texport GIT_INDEX_FILE\n    +\n            trap '\n    -\t\ttest -f \"$MESSAGE\" && rm \"$MESSAGE\"\n    +\t\ttest -f \"$MSG_FILE\" && rm \"$MSG_FILE\"\n    +\t\ttest -f \"$GIT_INDEX_FILE\" && rm \"$GIT_INDEX_FILE\"\n            ' 0\n\nAnother improvement is that the variable definitions were moved up before\nthe trap that uses these definitions.  This is a good change.\n\n    -\tGIT_NOTES_REF= git log -1 $COMMIT | sed \"s/^/#/\" > \"$MESSAGE\"\n    -\n    -\tGIT_INDEX_FILE=\"$MESSAGE\".idx\n    -\texport GIT_INDEX_FILE\n    +\tgit log -1 $COMMIT | sed \"s/^/#/\" > \"$MSG_FILE\"\n\nDscho's version exports an empty GIT_NOTES_REF, presumably to avoid\ntriggering the notes mechanism, while running this \"git log -1\", but yours\ndon't.  Is there a reason behind this change?\n\nThe rest are fallouts from s/MESSAGE/MSG_FILE/ renaming.\n\n    @@ -36,16 +37,16 @@ edit)\n            else\n                    PARENT=\"-p $CURRENT_HEAD\"\n                    git read-tree \"$GIT_NOTES_REF\" || die \"Could not read index\"\n    -\t\tgit cat-file blob :$COMMIT >> \"$MESSAGE\" 2> /dev/null\n    +\t\tgit cat-file blob :$COMMIT >> \"$MSG_FILE\" 2> /dev/null\n            fi\n\n            core_editor=\"$(git config core.editor)\"\n    -\t${GIT_EDITOR:-${core_editor:-${VISUAL:-${EDITOR:-vi}}}} \"$MESSAGE\"\n    +\t${GIT_EDITOR:-${core_editor:-${VISUAL:-${EDITOR:-vi}}}} \"$MSG_FILE\"\n\n    -\tgrep -v ^# < \"$MESSAGE\" | git stripspace > \"$MESSAGE\".processed\n    -\tmv \"$MESSAGE\".processed \"$MESSAGE\"\n    -\tif [ -s \"$MESSAGE\" ]; then\n    -\t\tBLOB=$(git hash-object -w \"$MESSAGE\") ||\n    +\tgrep -v ^# < \"$MSG_FILE\" | git stripspace > \"$MSG_FILE\".processed\n    +\tmv \"$MSG_FILE\".processed \"$MSG_FILE\"\n    +\tif [ -s \"$MSG_FILE\" ]; then\n    +\t\tBLOB=$(git hash-object -w \"$MSG_FILE\") ||\n                            die \"Could not write into object database\"\n                    git update-index --add --cacheinfo 0644 $BLOB $COMMIT ||\n                            die \"Could not write index\"\n\nI am a bit reluctant to take this to replace js/notes as-is until I hear\nanswers to these points (and others may have comments too).\n"},{"id":"114092","messageId":"200905161320.45426.johan@herland.net","threadId":"19365","inReplyTo":"7vpre9plwl.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 0/5] RESEND: git notes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-05-16T11:20:45Z","receivedAt":"2009-05-16T11:20:45Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Saturday 16 May 2009, Junio C Hamano wrote:\n> Johan Herland <johan@herland.net> writes:\n> > In sum, these 5 patches produce the exact same result as the original\n> > js/notes series (plus my 2 patches).\n>\n> Thanks, but you did no such thing, actually.\n>\n> The result from this sequence\n>\n> \tgit checkout -b jh/notes v1.6.3.1^0\n>         git am ./+jh-notes.mbox ;# these patches\n> \tgit checkout v1.6.3.1^0\n>         git merge js/notes ;# allow rerere to reapply the resolution\n>         git diff -R jh/notes^\n>\n> should be empty, or should show your improvements over what has been\n> queued in 'pu'.\n\nSorry for the screwup. The diff should in any case not be empty, since\nthe first patch (the cleanup/bugfix part) of my original 2 patches\n(which are NOT in js/notes on 'pu') has been squashed into the first 4\npatches of this new series.\n\nTherefore, the diff you quote below is simply this first patch from my\noriginal two-part series (the second patch of that series is now the\n5th patch in this series - adding '-m' and '-F' support to 'git notes\nedit').\n\n> Here is what I see:\n>\n>     diff --git b/git-notes.sh a/git-notes.sh\n>     index 6ec33c9..7c3b8b9 100755\n>     --- b/git-notes.sh\n>     +++ a/git-notes.sh\n>     @@ -20,15 +20,16 @@ edit)\n>                     die \"Refusing to edit notes in $GIT_NOTES_REF (outside of refs/notes/)\" fi\n>\n>     -\tMESSAGE=\"$GIT_DIR\"/new-notes-$COMMIT\n>     +\tMSG_FILE=\"$GIT_DIR/new-notes-$COMMIT\"\n>     +\tGIT_INDEX_FILE=\"MSG_FILE.idx\"\n>\n> Renaming of the variable MESSAGE to MSG_FILE may be an improvement, as\n> the former could have puzzled the reader if $MESSAGE contains the message\n> itself, or it has the name of a file that stores the message (the answer\n> is latter).\n\nIndeed. If you look at the next patch (adding support for '-m' and\n'-F'), you will see that I reuse $MESSAGE to hold the _actual_ message\n(given by '-m' or '-F').\n\n> But I think there is a regression with a missing '$' here.\n\nYes. Thanks for catching.\n\n>     +\texport GIT_INDEX_FILE\n>     +\n>             trap '\n>     -\t\ttest -f \"$MESSAGE\" && rm \"$MESSAGE\"\n>     +\t\ttest -f \"$MSG_FILE\" && rm \"$MSG_FILE\"\n>     +\t\ttest -f \"$GIT_INDEX_FILE\" && rm \"$GIT_INDEX_FILE\"\n>             ' 0\n>\n> Another improvement is that the variable definitions were moved up before\n> the trap that uses these definitions.  This is a good change.\n>\n>     -\tGIT_NOTES_REF= git log -1 $COMMIT | sed \"s/^/#/\" > \"$MESSAGE\"\n>     -\n>     -\tGIT_INDEX_FILE=\"$MESSAGE\".idx\n>     -\texport GIT_INDEX_FILE\n>     +\tgit log -1 $COMMIT | sed \"s/^/#/\" > \"$MSG_FILE\"\n>\n> Dscho's version exports an empty GIT_NOTES_REF, presumably to avoid\n> triggering the notes mechanism, while running this \"git log -1\", but\n> yours don't.  Is there a reason behind this change?\n\nOh, so _that's_ what he did... I was puzzled by that \"stray\"\n'GIT_NOTES_REF= '...\n\nYet another screwup on my part.\n\n> The rest are fallouts from s/MESSAGE/MSG_FILE/ renaming.\n>\n>     @@ -36,16 +37,16 @@ edit)\n>             else\n>                     PARENT=\"-p $CURRENT_HEAD\"\n>                     git read-tree \"$GIT_NOTES_REF\" || die \"Could not read index\"\n>     -\t\tgit cat-file blob :$COMMIT >> \"$MESSAGE\" 2> /dev/null\n>     +\t\tgit cat-file blob :$COMMIT >> \"$MSG_FILE\" 2> /dev/null\n>             fi\n>\n>             core_editor=\"$(git config core.editor)\"\n>     -\t${GIT_EDITOR:-${core_editor:-${VISUAL:-${EDITOR:-vi}}}} \"$MESSAGE\"\n>     +\t${GIT_EDITOR:-${core_editor:-${VISUAL:-${EDITOR:-vi}}}} \"$MSG_FILE\"\n>\n>     -\tgrep -v ^# < \"$MESSAGE\" | git stripspace > \"$MESSAGE\".processed\n>     -\tmv \"$MESSAGE\".processed \"$MESSAGE\"\n>     -\tif [ -s \"$MESSAGE\" ]; then\n>     -\t\tBLOB=$(git hash-object -w \"$MESSAGE\") ||\n>     +\tgrep -v ^# < \"$MSG_FILE\" | git stripspace > \"$MSG_FILE\".processed\n>     +\tmv \"$MSG_FILE\".processed \"$MSG_FILE\"\n>     +\tif [ -s \"$MSG_FILE\" ]; then\n>     +\t\tBLOB=$(git hash-object -w \"$MSG_FILE\") ||\n>       \t\tdie \"Could not write into object database\"\n>       \tgit update-index --add --cacheinfo 0644 $BLOB $COMMIT ||\n>       \t\tdie \"Could not write index\"\n>\n> I am a bit reluctant to take this to replace js/notes as-is until I hear\n> answers to these points (and others may have comments too).\n\nThanks for the review.\nI will send an updated series with the above fixes.\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"114093","messageId":"cover.1242473357.git.johan@herland.net","threadId":"19365","inReplyTo":"200905161320.45426.johan@herland.net","subject":"[PATCHv2 0/5] RESEND: git notes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-05-16T11:44:12Z","receivedAt":"2009-05-16T11:44:12Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Hi,\n\nDscho has asked me to take over the responsibility for the js/notes\npatch series.\n\nThe following is a re-roll and resend of the patch series currently\nin 'pu', plus my own 2 patches for adding support for '-m' and '-F'\nto 'git notes edit'.\n\nOn advice from Dscho, I have squashed the current bugfix and cleanup\npatches in js/notes (plus the first of my two patches) into the first\n4 \"main\" patches. As a result the original 15 + 2 patch series is now\ndown to 5 (4 + 1) patches.\n\nIn sum, the first 4 patches in this series is equivalent to the\noriginal js/notes series plus the first of my 2 patches. The final 5th\npatch is equivalent to my second patch (adding support for '-m' and\n'-F' to 'git notes edit').\n\nI have taken the liberty of squashing the various Signed-off-by tags\n(along with their corresponding patches) into these 5 new patches.\nI hope this is OK with everybody. If not, I apologize; please tell me,\nand I will re-send.\n\n\nHave fun! :)\n\n...Johan\n\n\nJohan Herland (1):\n  Teach \"-m <msg>\" and \"-F <file>\" to \"git notes edit\"\n\nJohannes Schindelin (4):\n  Introduce commit notes\n  Add a script to edit/inspect notes\n  Speed up git notes lookup\n  Add an expensive test for git-notes\n\n .gitignore                       |    1 +\n Documentation/config.txt         |   13 +++\n Documentation/git-notes.txt      |   60 ++++++++++++++\n Makefile                         |    3 +\n cache.h                          |    4 +\n command-list.txt                 |    1 +\n commit.c                         |    1 +\n config.c                         |    5 +\n environment.c                    |    1 +\n git-notes.sh                     |  121 ++++++++++++++++++++++++++++\n notes.c                          |  160 ++++++++++++++++++++++++++++++++++++++\n notes.h                          |    7 ++\n pretty.c                         |    5 +\n t/t3301-notes.sh                 |  149 +++++++++++++++++++++++++++++++++++\n t/t3302-notes-index-expensive.sh |   98 +++++++++++++++++++++++\n 15 files changed, 629 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-notes.txt\n create mode 100755 git-notes.sh\n create mode 100644 notes.c\n create mode 100644 notes.h\n create mode 100755 t/t3301-notes.sh\n create mode 100755 t/t3302-notes-index-expensive.sh\n"},{"id":"114094","messageId":"e4bca1f4de44d45926d71b7c968253fe87e9ce37.1242473357.git.johan@herland.net","threadId":"19365","inReplyTo":"200905161320.45426.johan@herland.net","subject":"[PATCHv2 1/5] Introduce commit notes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-05-16T11:44:13Z","receivedAt":"2009-05-16T11:44:13Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"From: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\nCommit notes are blobs which are shown together with the commit\nmessage.  These blobs are taken from the notes ref, which you can\nconfigure by the config variable core.notesRef, which in turn can\nbe overridden by the environment variable GIT_NOTES_REF.\n\nThe notes ref is a branch which contains \"files\" whose names are\nthe names of the corresponding commits (i.e. the SHA-1).\n\nThe rationale for putting this information into a ref is this: we\nwant to be able to fetch and possibly union-merge the notes,\nmaybe even look at the date when a note was introduced, and we\nwant to store them efficiently together with the other objects.\n\n[tr: fix core.notesRef documentation]\n[tv: fix printing of multi-line notes]\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\nSigned-off-by: Tor Arne Vestbø <tavestbo@trolltech.com>\nSigned-off-by: Johan Herland <johan@herland.net>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config.txt |   13 ++++++++\n Makefile                 |    2 +\n cache.h                  |    4 ++\n commit.c                 |    1 +\n config.c                 |    5 +++\n environment.c            |    1 +\n notes.c                  |   69 ++++++++++++++++++++++++++++++++++++++++++++++\n notes.h                  |    7 ++++\n pretty.c                 |    5 +++\n 9 files changed, 107 insertions(+), 0 deletions(-)\n create mode 100644 notes.c\n create mode 100644 notes.h\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 5dcad94..6875ecd 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -438,6 +438,19 @@ On some file system/operating system combinations, this is unreliable.\n Set this config setting to 'rename' there; However, This will remove the\n check that makes sure that existing object files will not get overwritten.\n \n+core.notesRef::\n+\tWhen showing commit messages, also show notes which are stored in\n+\tthe given ref.  This ref is expected to contain files named\n+\tafter the full SHA-1 of the commit they annotate.\n++\n+If such a file exists in the given ref, the referenced blob is read, and\n+appended to the commit message, separated by a \"Notes:\" line.  If the\n+given ref itself does not exist, it is not an error, but means that no\n+notes should be printed.\n++\n+This setting defaults to \"refs/notes/commits\", and can be overridden by\n+the `GIT_NOTES_REF` environment variable.\n+\n alias.*::\n \tCommand aliases for the linkgit:git[1] command wrapper - e.g.\n \tafter defining \"alias.last = cat-file commit HEAD\", the invocation\ndiff --git a/Makefile b/Makefile\nindex 6e21643..59e102c 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -406,6 +406,7 @@ LIB_H += ll-merge.h\n LIB_H += log-tree.h\n LIB_H += mailmap.h\n LIB_H += merge-recursive.h\n+LIB_H += notes.h\n LIB_H += object.h\n LIB_H += pack.h\n LIB_H += pack-refs.h\n@@ -489,6 +490,7 @@ LIB_OBJS += match-trees.o\n LIB_OBJS += merge-file.o\n LIB_OBJS += merge-recursive.o\n LIB_OBJS += name-hash.o\n+LIB_OBJS += notes.o\n LIB_OBJS += object.o\n LIB_OBJS += pack-check.o\n LIB_OBJS += pack-refs.o\ndiff --git a/cache.h b/cache.h\nindex b8503ad..8a830a6 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -371,6 +371,8 @@ static inline enum object_type object_type(unsigned int mode)\n #define GITATTRIBUTES_FILE \".gitattributes\"\n #define INFOATTRIBUTES_FILE \"info/attributes\"\n #define ATTRIBUTE_MACRO_PREFIX \"[attr]\"\n+#define GIT_NOTES_REF_ENVIRONMENT \"GIT_NOTES_REF\"\n+#define GIT_NOTES_DEFAULT_REF \"refs/notes/commits\"\n \n extern int is_bare_repository_cfg;\n extern int is_bare_repository(void);\n@@ -561,6 +563,8 @@ enum object_creation_mode {\n \n extern enum object_creation_mode object_creation_mode;\n \n+extern char *notes_ref_name;\n+\n #define GIT_REPO_VERSION 0\n extern int repository_format_version;\n extern int check_repository_format(void);\ndiff --git a/commit.c b/commit.c\nindex aa3b35b..cf72143 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -5,6 +5,7 @@\n #include \"utf8.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n+#include \"notes.h\"\n \n int save_commit_buffer = 1;\n \ndiff --git a/config.c b/config.c\nindex 1682273..d82b580 100644\n--- a/config.c\n+++ b/config.c\n@@ -469,6 +469,11 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.notesref\")) {\n+\t\tnotes_ref_name = xstrdup(value);\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(var, \"core.pager\"))\n \t\treturn git_config_string(&pager_program, var, value);\n \ndiff --git a/environment.c b/environment.c\nindex 801a005..f445cfd 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -50,6 +50,7 @@ enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;\n \n /* Parallel index stat data preload? */\n int core_preload_index = 0;\n+char *notes_ref_name;\n \n /* This is set by setup_git_dir_gently() and/or git_default_config() */\n char *git_work_tree_cfg;\ndiff --git a/notes.c b/notes.c\nnew file mode 100644\nindex 0000000..b0cf553\n--- /dev/null\n+++ b/notes.c\n@@ -0,0 +1,69 @@\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"notes.h\"\n+#include \"refs.h\"\n+#include \"utf8.h\"\n+#include \"strbuf.h\"\n+\n+static int initialized;\n+\n+void get_commit_notes(const struct commit *commit, struct strbuf *sb,\n+\t\tconst char *output_encoding)\n+{\n+\tstatic const char *utf8 = \"utf-8\";\n+\tstruct strbuf name = STRBUF_INIT;\n+\tconst char *hex;\n+\tunsigned char sha1[20];\n+\tchar *msg, *msg_p;\n+\tunsigned long linelen, msglen;\n+\tenum object_type type;\n+\n+\tif (!initialized) {\n+\t\tconst char *env = getenv(GIT_NOTES_REF_ENVIRONMENT);\n+\t\tif (env)\n+\t\t\tnotes_ref_name = getenv(GIT_NOTES_REF_ENVIRONMENT);\n+\t\telse if (!notes_ref_name)\n+\t\t\tnotes_ref_name = GIT_NOTES_DEFAULT_REF;\n+\t\tif (notes_ref_name && read_ref(notes_ref_name, sha1))\n+\t\t\tnotes_ref_name = NULL;\n+\t\tinitialized = 1;\n+\t}\n+\n+\tif (!notes_ref_name)\n+\t\treturn;\n+\n+\tstrbuf_addf(&name, \"%s:%s\", notes_ref_name,\n+\t\t\tsha1_to_hex(commit->object.sha1));\n+\tif (get_sha1(name.buf, sha1))\n+\t\treturn;\n+\n+\tif (!(msg = read_sha1_file(sha1, &type, &msglen)) || !msglen ||\n+\t\t\ttype != OBJ_BLOB)\n+\t\treturn;\n+\n+\tif (output_encoding && *output_encoding &&\n+\t\t\tstrcmp(utf8, output_encoding)) {\n+\t\tchar *reencoded = reencode_string(msg, output_encoding, utf8);\n+\t\tif (reencoded) {\n+\t\t\tfree(msg);\n+\t\t\tmsg = reencoded;\n+\t\t\tmsglen = strlen(msg);\n+\t\t}\n+\t}\n+\n+\t/* we will end the annotation by a newline anyway */\n+\tif (msglen && msg[msglen - 1] == '\\n')\n+\t\tmsglen--;\n+\n+\tstrbuf_addstr(sb, \"\\nNotes:\\n\");\n+\n+\tfor (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {\n+\t\tlinelen = strchrnul(msg_p, '\\n') - msg_p;\n+\n+\t\tstrbuf_addstr(sb, \"    \");\n+\t\tstrbuf_add(sb, msg_p, linelen);\n+\t\tstrbuf_addch(sb, '\\n');\n+\t}\n+\n+\tfree(msg);\n+}\ndiff --git a/notes.h b/notes.h\nnew file mode 100644\nindex 0000000..79d21b6\n--- /dev/null\n+++ b/notes.h\n@@ -0,0 +1,7 @@\n+#ifndef NOTES_H\n+#define NOTES_H\n+\n+void get_commit_notes(const struct commit *commit, struct strbuf *sb,\n+\t\tconst char *output_encoding);\n+\n+#endif\ndiff --git a/pretty.c b/pretty.c\nindex a0ef356..d30a964 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -6,6 +6,7 @@\n #include \"string-list.h\"\n #include \"mailmap.h\"\n #include \"log-tree.h\"\n+#include \"notes.h\"\n #include \"color.h\"\n \n static char *user_format;\n@@ -963,5 +964,9 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,\n \t */\n \tif (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)\n \t\tstrbuf_addch(sb, '\\n');\n+\n+\tif (fmt != CMIT_FMT_ONELINE)\n+\t\tget_commit_notes(commit, sb, encoding);\n+\n \tfree(reencoded);\n }\n-- \n1.6.3.rc0.1.gf800\n"},{"id":"114095","messageId":"dd22ea95fb7fbf8cfee5b0ec482af3395b408175.1242473357.git.johan@herland.net","threadId":"19365","inReplyTo":"200905161320.45426.johan@herland.net","subject":"[PATCHv2 2/5] Add a script to edit/inspect notes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-05-16T11:44:14Z","receivedAt":"2009-05-16T11:44:14Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"From: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\nThe script 'git notes' allows you to edit and show commit notes, by\ncalling either\n\n\tgit notes show <commit>\n\nor\n\n\tgit notes edit <commit>\n\n[tv: fix printing of multi-line notes]\n[mg: test and handle empty notes gracefully]\n[tr: only clean up message file when editing]\n[tr: use GIT_EDITOR and core.editor over VISUAL/EDITOR]\n[tr: t3301: fix confusing quoting in test for valid notes ref]\n[tr: t3301: use test_must_fail instead of !]\n[tr: refuse to edit notes outside refs/notes/]\n[jc: tests: fix \"export var=val\"]\n[cc: Documentation: fix 'linkgit' macro in \"git-notes.txt\"]\n[jh: Minor cleanup and bugfixing in git-notes.sh (v2)]\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Tor Arne Vestbø <tavestbo@trolltech.com>\nSigned-off-by: Michael J Gruber <git@drmicha.warpmail.net>\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Johan Herland <johan@herland.net>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n .gitignore                  |    1 +\n Documentation/git-notes.txt |   46 +++++++++++++++++\n Makefile                    |    1 +\n command-list.txt            |    1 +\n git-notes.sh                |   73 +++++++++++++++++++++++++++\n t/t3301-notes.sh            |  114 +++++++++++++++++++++++++++++++++++++++++++\n 6 files changed, 236 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-notes.txt\n create mode 100755 git-notes.sh\n create mode 100755 t/t3301-notes.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex 41c0b20..13221e7 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -86,6 +86,7 @@ git-mktag\n git-mktree\n git-name-rev\n git-mv\n+git-notes\n git-pack-redundant\n git-pack-objects\n git-pack-refs\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nnew file mode 100644\nindex 0000000..7136016\n--- /dev/null\n+++ b/Documentation/git-notes.txt\n@@ -0,0 +1,46 @@\n+git-notes(1)\n+============\n+\n+NAME\n+----\n+git-notes - Add/inspect commit notes\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git-notes' (edit | show) [commit]\n+\n+DESCRIPTION\n+-----------\n+This command allows you to add notes to commit messages, without\n+changing the commit.  To discern these notes from the message stored\n+in the commit object, the notes are indented like the message, after\n+an unindented line saying \"Notes:\".\n+\n+To disable commit notes, you have to set the config variable\n+core.notesRef to the empty string.  Alternatively, you can set it\n+to a different ref, something like \"refs/notes/bugzilla\".  This setting\n+can be overridden by the environment variable \"GIT_NOTES_REF\".\n+\n+\n+SUBCOMMANDS\n+-----------\n+\n+edit::\n+\tEdit the notes for a given commit (defaults to HEAD).\n+\n+show::\n+\tShow the notes for a given commit (defaults to HEAD).\n+\n+\n+Author\n+------\n+Written by Johannes Schindelin <johannes.schindelin@gmx.de>\n+\n+Documentation\n+-------------\n+Documentation by Johannes Schindelin\n+\n+GIT\n+---\n+Part of the linkgit:git[7] suite\ndiff --git a/Makefile b/Makefile\nindex 59e102c..1d91a04 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -295,6 +295,7 @@ SCRIPT_SH += git-merge-one-file.sh\n SCRIPT_SH += git-merge-resolve.sh\n SCRIPT_SH += git-mergetool.sh\n SCRIPT_SH += git-mergetool--lib.sh\n+SCRIPT_SH += git-notes.sh\n SCRIPT_SH += git-parse-remote.sh\n SCRIPT_SH += git-pull.sh\n SCRIPT_SH += git-quiltimport.sh\ndiff --git a/command-list.txt b/command-list.txt\nindex fb03a2e..4296941 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -74,6 +74,7 @@ git-mktag                               plumbingmanipulators\n git-mktree                              plumbingmanipulators\n git-mv                                  mainporcelain common\n git-name-rev                            plumbinginterrogators\n+git-notes                               mainporcelain\n git-pack-objects                        plumbingmanipulators\n git-pack-redundant                      plumbinginterrogators\n git-pack-refs                           ancillarymanipulators\ndiff --git a/git-notes.sh b/git-notes.sh\nnew file mode 100755\nindex 0000000..f06c254\n--- /dev/null\n+++ b/git-notes.sh\n@@ -0,0 +1,73 @@\n+#!/bin/sh\n+\n+USAGE=\"(edit | show) [commit]\"\n+. git-sh-setup\n+\n+test -n \"$3\" && usage\n+\n+test -z \"$1\" && usage\n+ACTION=\"$1\"; shift\n+\n+test -z \"$GIT_NOTES_REF\" && GIT_NOTES_REF=\"$(git config core.notesref)\"\n+test -z \"$GIT_NOTES_REF\" && GIT_NOTES_REF=\"refs/notes/commits\"\n+\n+COMMIT=$(git rev-parse --verify --default HEAD \"$@\") ||\n+die \"Invalid commit: $@\"\n+\n+case \"$ACTION\" in\n+edit)\n+\tif [ \"${GIT_NOTES_REF#refs/notes/}\" = \"$GIT_NOTES_REF\" ]; then\n+\t\tdie \"Refusing to edit notes in $GIT_NOTES_REF (outside of refs/notes/)\"\n+\tfi\n+\n+\tMSG_FILE=\"$GIT_DIR/new-notes-$COMMIT\"\n+\tGIT_INDEX_FILE=\"$MSG_FILE.idx\"\n+\texport GIT_INDEX_FILE\n+\n+\ttrap '\n+\t\ttest -f \"$MSG_FILE\" && rm \"$MSG_FILE\"\n+\t\ttest -f \"$GIT_INDEX_FILE\" && rm \"$GIT_INDEX_FILE\"\n+\t' 0\n+\n+\tGIT_NOTES_REF= git log -1 $COMMIT | sed \"s/^/#/\" > \"$MSG_FILE\"\n+\n+\tCURRENT_HEAD=$(git show-ref \"$GIT_NOTES_REF\" | cut -f 1 -d ' ')\n+\tif [ -z \"$CURRENT_HEAD\" ]; then\n+\t\tPARENT=\n+\telse\n+\t\tPARENT=\"-p $CURRENT_HEAD\"\n+\t\tgit read-tree \"$GIT_NOTES_REF\" || die \"Could not read index\"\n+\t\tgit cat-file blob :$COMMIT >> \"$MSG_FILE\" 2> /dev/null\n+\tfi\n+\n+\tcore_editor=\"$(git config core.editor)\"\n+\t${GIT_EDITOR:-${core_editor:-${VISUAL:-${EDITOR:-vi}}}} \"$MSG_FILE\"\n+\n+\tgrep -v ^# < \"$MSG_FILE\" | git stripspace > \"$MSG_FILE\".processed\n+\tmv \"$MSG_FILE\".processed \"$MSG_FILE\"\n+\tif [ -s \"$MSG_FILE\" ]; then\n+\t\tBLOB=$(git hash-object -w \"$MSG_FILE\") ||\n+\t\t\tdie \"Could not write into object database\"\n+\t\tgit update-index --add --cacheinfo 0644 $BLOB $COMMIT ||\n+\t\t\tdie \"Could not write index\"\n+\telse\n+\t\ttest -z \"$CURRENT_HEAD\" &&\n+\t\t\tdie \"Will not initialise with empty tree\"\n+\t\tgit update-index --force-remove $COMMIT ||\n+\t\t\tdie \"Could not update index\"\n+\tfi\n+\n+\tTREE=$(git write-tree) || die \"Could not write tree\"\n+\tNEW_HEAD=$(echo Annotate $COMMIT | git commit-tree $TREE $PARENT) ||\n+\t\tdie \"Could not annotate\"\n+\tgit update-ref -m \"Annotate $COMMIT\" \\\n+\t\t\"$GIT_NOTES_REF\" $NEW_HEAD $CURRENT_HEAD\n+;;\n+show)\n+\tgit rev-parse -q --verify \"$GIT_NOTES_REF\":$COMMIT > /dev/null ||\n+\t\tdie \"No note for commit $COMMIT.\"\n+\tgit show \"$GIT_NOTES_REF\":$COMMIT\n+;;\n+*)\n+\tusage\n+esac\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nnew file mode 100755\nindex 0000000..73e53be\n--- /dev/null\n+++ b/t/t3301-notes.sh\n@@ -0,0 +1,114 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2007 Johannes E. Schindelin\n+#\n+\n+test_description='Test commit notes'\n+\n+. ./test-lib.sh\n+\n+cat > fake_editor.sh << \\EOF\n+echo \"$MSG\" > \"$1\"\n+echo \"$MSG\" >& 2\n+EOF\n+chmod a+x fake_editor.sh\n+VISUAL=./fake_editor.sh\n+export VISUAL\n+\n+test_expect_success 'cannot annotate non-existing HEAD' '\n+\t(MSG=3 && export MSG && test_must_fail git notes edit)\n+'\n+\n+test_expect_success setup '\n+\t: > a1 &&\n+\tgit add a1 &&\n+\ttest_tick &&\n+\tgit commit -m 1st &&\n+\t: > a2 &&\n+\tgit add a2 &&\n+\ttest_tick &&\n+\tgit commit -m 2nd\n+'\n+\n+test_expect_success 'need valid notes ref' '\n+\t(MSG=1 GIT_NOTES_REF=/ && export MSG GIT_NOTES_REF &&\n+\t test_must_fail git notes edit) &&\n+\t(MSG=2 GIT_NOTES_REF=/ && export MSG GIT_NOTES_REF &&\n+\t test_must_fail git notes show)\n+'\n+\n+test_expect_success 'refusing to edit in refs/heads/' '\n+\t(MSG=1 GIT_NOTES_REF=refs/heads/bogus &&\n+\t export MSG GIT_NOTES_REF &&\n+\t test_must_fail git notes edit)\n+'\n+\n+test_expect_success 'refusing to edit in refs/remotes/' '\n+\t(MSG=1 GIT_NOTES_REF=refs/remotes/bogus &&\n+\t export MSG GIT_NOTES_REF &&\n+\t test_must_fail git notes edit)\n+'\n+\n+# 1 indicates caught gracefully by die, 128 means git-show barked\n+test_expect_success 'handle empty notes gracefully' '\n+\tgit notes show ; test 1 = $?\n+'\n+\n+test_expect_success 'create notes' '\n+\tgit config core.notesRef refs/notes/commits &&\n+\tMSG=b1 git notes edit &&\n+\ttest ! -f .git/new-notes &&\n+\ttest 1 = $(git ls-tree refs/notes/commits | wc -l) &&\n+\ttest b1 = $(git notes show) &&\n+\tgit show HEAD^ &&\n+\ttest_must_fail git notes show HEAD^\n+'\n+\n+cat > expect << EOF\n+commit 268048bfb8a1fb38e703baceb8ab235421bf80c5\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:14:13 2005 -0700\n+\n+    2nd\n+\n+Notes:\n+    b1\n+EOF\n+\n+test_expect_success 'show notes' '\n+\t! (git cat-file commit HEAD | grep b1) &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+test_expect_success 'create multi-line notes (setup)' '\n+\t: > a3 &&\n+\tgit add a3 &&\n+\ttest_tick &&\n+\tgit commit -m 3rd &&\n+\tMSG=\"b3\n+c3c3c3c3\n+d3d3d3\" git notes edit\n+'\n+\n+cat > expect-multiline << EOF\n+commit 1584215f1d29c65e99c6c6848626553fdd07fd75\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:15:13 2005 -0700\n+\n+    3rd\n+\n+Notes:\n+    b3\n+    c3c3c3c3\n+    d3d3d3\n+EOF\n+\n+printf \"\\n\" >> expect-multiline\n+cat expect >> expect-multiline\n+\n+test_expect_success 'show multi-line notes' '\n+\tgit log -2 > output &&\n+\ttest_cmp expect-multiline output\n+'\n+\n+test_done\n-- \n1.6.3.rc0.1.gf800\n"},{"id":"114096","messageId":"1534aac028f3b64578b6f07858cf6108736a3e32.1242473357.git.johan@herland.net","threadId":"19365","inReplyTo":"200905161320.45426.johan@herland.net","subject":"[PATCHv2 3/5] Speed up git notes lookup","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-05-16T11:44:15Z","receivedAt":"2009-05-16T11:44:15Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"From: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\nTo avoid looking up each and every commit in the notes ref's tree\nobject, which is very expensive, speed things up by slurping the tree\nobject's contents into a hash_map.\n\nThe idea for the hashmap singleton is from David Reiss, initial\nbenchmarking by Jeff King.\n\nNote: the implementation allows for arbitrary entries in the notes\ntree object, ignoring those that do not reference a valid object.  This\nallows you to annotate arbitrary branches, or objects.\n\n[jc: fixed an obvious error in initialize_hash_map()]\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Johan Herland <johan@herland.net>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n notes.c |  113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------\n 1 files changed, 102 insertions(+), 11 deletions(-)\n\ndiff --git a/notes.c b/notes.c\nindex b0cf553..bd73784 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -4,16 +4,112 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n #include \"strbuf.h\"\n+#include \"tree-walk.h\"\n+\n+struct entry {\n+\tunsigned char commit_sha1[20];\n+\tunsigned char notes_sha1[20];\n+};\n+\n+struct hash_map {\n+\tstruct entry *entries;\n+\toff_t count, size;\n+};\n \n static int initialized;\n+static struct hash_map hash_map;\n+\n+static int hash_index(struct hash_map *map, const unsigned char *sha1)\n+{\n+\tint i = ((*(unsigned int *)sha1) % map->size);\n+\n+\tfor (;;) {\n+\t\tunsigned char *current = map->entries[i].commit_sha1;\n+\n+\t\tif (!hashcmp(sha1, current))\n+\t\t\treturn i;\n+\n+\t\tif (is_null_sha1(current))\n+\t\t\treturn -1 - i;\n+\n+\t\tif (++i == map->size)\n+\t\t\ti = 0;\n+\t}\n+}\n+\n+static void add_entry(const unsigned char *commit_sha1,\n+\t\tconst unsigned char *notes_sha1)\n+{\n+\tint index;\n+\n+\tif (hash_map.count + 1 > hash_map.size >> 1) {\n+\t\tint i, old_size = hash_map.size;\n+\t\tstruct entry *old = hash_map.entries;\n+\n+\t\thash_map.size = old_size ? old_size << 1 : 64;\n+\t\thash_map.entries = (struct entry *)\n+\t\t\txcalloc(sizeof(struct entry), hash_map.size);\n+\n+\t\tfor (i = 0; i < old_size; i++)\n+\t\t\tif (!is_null_sha1(old[i].commit_sha1)) {\n+\t\t\t\tindex = -1 - hash_index(&hash_map,\n+\t\t\t\t\t\told[i].commit_sha1);\n+\t\t\t\tmemcpy(hash_map.entries + index, old + i,\n+\t\t\t\t\tsizeof(struct entry));\n+\t\t\t}\n+\t\tfree(old);\n+\t}\n+\n+\tindex = hash_index(&hash_map, commit_sha1);\n+\tif (index < 0) {\n+\t\tindex = -1 - index;\n+\t\thash_map.count++;\n+\t}\n+\n+\thashcpy(hash_map.entries[index].commit_sha1, commit_sha1);\n+\thashcpy(hash_map.entries[index].notes_sha1, notes_sha1);\n+}\n+\n+static void initialize_hash_map(const char *notes_ref_name)\n+{\n+\tunsigned char sha1[20], commit_sha1[20];\n+\tunsigned mode;\n+\tstruct tree_desc desc;\n+\tstruct name_entry entry;\n+\tvoid *buf;\n+\n+\tif (!notes_ref_name || read_ref(notes_ref_name, commit_sha1) ||\n+\t    get_tree_entry(commit_sha1, \"\", sha1, &mode))\n+\t\treturn;\n+\n+\tbuf = fill_tree_descriptor(&desc, sha1);\n+\tif (!buf)\n+\t\tdie(\"Could not read %s for notes-index\", sha1_to_hex(sha1));\n+\n+\twhile (tree_entry(&desc, &entry))\n+\t\tif (!get_sha1(entry.path, commit_sha1))\n+\t\t\tadd_entry(commit_sha1, entry.sha1);\n+\tfree(buf);\n+}\n+\n+static unsigned char *lookup_notes(const unsigned char *commit_sha1)\n+{\n+\tint index;\n+\n+\tif (!hash_map.size)\n+\t\treturn NULL;\n+\n+\tindex = hash_index(&hash_map, commit_sha1);\n+\tif (index < 0)\n+\t\treturn NULL;\n+\treturn hash_map.entries[index].notes_sha1;\n+}\n \n void get_commit_notes(const struct commit *commit, struct strbuf *sb,\n \t\tconst char *output_encoding)\n {\n \tstatic const char *utf8 = \"utf-8\";\n-\tstruct strbuf name = STRBUF_INIT;\n-\tconst char *hex;\n-\tunsigned char sha1[20];\n+\tunsigned char *sha1;\n \tchar *msg, *msg_p;\n \tunsigned long linelen, msglen;\n \tenum object_type type;\n@@ -24,17 +120,12 @@ void get_commit_notes(const struct commit *commit, struct strbuf *sb,\n \t\t\tnotes_ref_name = getenv(GIT_NOTES_REF_ENVIRONMENT);\n \t\telse if (!notes_ref_name)\n \t\t\tnotes_ref_name = GIT_NOTES_DEFAULT_REF;\n-\t\tif (notes_ref_name && read_ref(notes_ref_name, sha1))\n-\t\t\tnotes_ref_name = NULL;\n+\t\tinitialize_hash_map(notes_ref_name);\n \t\tinitialized = 1;\n \t}\n \n-\tif (!notes_ref_name)\n-\t\treturn;\n-\n-\tstrbuf_addf(&name, \"%s:%s\", notes_ref_name,\n-\t\t\tsha1_to_hex(commit->object.sha1));\n-\tif (get_sha1(name.buf, sha1))\n+\tsha1 = lookup_notes(commit->object.sha1);\n+\tif (!sha1)\n \t\treturn;\n \n \tif (!(msg = read_sha1_file(sha1, &type, &msglen)) || !msglen ||\n-- \n1.6.3.rc0.1.gf800\n"},{"id":"114098","messageId":"81845dbcafeabb34a81e4c59e390aaaa3fc604ea.1242473357.git.johan@herland.net","threadId":"19365","inReplyTo":"200905161320.45426.johan@herland.net","subject":"[PATCHv2 4/5] Add an expensive test for git-notes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-05-16T11:44:16Z","receivedAt":"2009-05-16T11:44:16Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"From: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\ngit-notes have the potential of being pretty expensive, so test with\na lot of commits.  A lot.  So to make things cheaper, you have to\nopt-in explicitely, by setting the environment variable\nGIT_NOTES_TIMING_TESTS.\n\n[jc: tests: fix \"export var=val\"]\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Johan Herland <johan@herland.net>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n t/t3302-notes-index-expensive.sh |   98 ++++++++++++++++++++++++++++++++++++++\n 1 files changed, 98 insertions(+), 0 deletions(-)\n create mode 100755 t/t3302-notes-index-expensive.sh\n\ndiff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh\nnew file mode 100755\nindex 0000000..0ef3e95\n--- /dev/null\n+++ b/t/t3302-notes-index-expensive.sh\n@@ -0,0 +1,98 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2007 Johannes E. Schindelin\n+#\n+\n+test_description='Test commit notes index (expensive!)'\n+\n+. ./test-lib.sh\n+\n+test -z \"$GIT_NOTES_TIMING_TESTS\" && {\n+\tsay Skipping timing tests\n+\ttest_done\n+\texit\n+}\n+\n+create_repo () {\n+\tnumber_of_commits=$1\n+\tnr=0\n+\tparent=\n+\ttest -d .git || {\n+\tgit init &&\n+\ttree=$(git write-tree) &&\n+\twhile [ $nr -lt $number_of_commits ]; do\n+\t\ttest_tick &&\n+\t\tcommit=$(echo $nr | git commit-tree $tree $parent) ||\n+\t\t\treturn\n+\t\tparent=\"-p $commit\"\n+\t\tnr=$(($nr+1))\n+\tdone &&\n+\tgit update-ref refs/heads/master $commit &&\n+\t{\n+\t\tGIT_INDEX_FILE=.git/temp; export GIT_INDEX_FILE;\n+\t\tgit rev-list HEAD | cat -n | sed \"s/^[ \t][ \t]*/ /g\" |\n+\t\twhile read nr sha1; do\n+\t\t\tblob=$(echo note $nr | git hash-object -w --stdin) &&\n+\t\t\techo $sha1 | sed \"s/^/0644 $blob 0\t/\"\n+\t\tdone | git update-index --index-info &&\n+\t\ttree=$(git write-tree) &&\n+\t\ttest_tick &&\n+\t\tcommit=$(echo notes | git commit-tree $tree) &&\n+\t\tgit update-ref refs/notes/commits $commit\n+\t} &&\n+\tgit config core.notesRef refs/notes/commits\n+\t}\n+}\n+\n+test_notes () {\n+\tcount=$1 &&\n+\tgit config core.notesRef refs/notes/commits &&\n+\tgit log | grep \"^    \" > output &&\n+\ti=1 &&\n+\twhile [ $i -le $count ]; do\n+\t\techo \"    $(($count-$i))\" &&\n+\t\techo \"    note $i\" &&\n+\t\ti=$(($i+1));\n+\tdone > expect &&\n+\tgit diff expect output\n+}\n+\n+cat > time_notes << \\EOF\n+\tmode=$1\n+\ti=1\n+\twhile [ $i -lt $2 ]; do\n+\t\tcase $1 in\n+\t\tno-notes)\n+\t\t\tGIT_NOTES_REF=non-existing; export GIT_NOTES_REF\n+\t\t;;\n+\t\tnotes)\n+\t\t\tunset GIT_NOTES_REF\n+\t\t;;\n+\t\tesac\n+\t\tgit log >/dev/null\n+\t\ti=$(($i+1))\n+\tdone\n+EOF\n+\n+time_notes () {\n+\tfor mode in no-notes notes\n+\tdo\n+\t\techo $mode\n+\t\t/usr/bin/time sh ../time_notes $mode $1\n+\tdone\n+}\n+\n+for count in 10 100 1000 10000; do\n+\n+\tmkdir $count\n+\t(cd $count;\n+\n+\ttest_expect_success \"setup $count\" \"create_repo $count\"\n+\n+\ttest_expect_success 'notes work' \"test_notes $count\"\n+\n+\ttest_expect_success 'notes timing' \"time_notes 100\"\n+\t)\n+done\n+\n+test_done\n-- \n1.6.3.rc0.1.gf800\n"},{"id":"114097","messageId":"3fa832add6be9ee536137a73a55a3b9add742117.1242473357.git.johan@herland.net","threadId":"19365","inReplyTo":"200905161320.45426.johan@herland.net","subject":"[PATCHv2 5/5] Teach \"-m <msg>\" and \"-F <file>\" to \"git notes edit\"","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-05-16T11:44:17Z","receivedAt":"2009-05-16T11:44:17Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"The \"-m\" and \"-F\" options are already the established method\n(in both git-commit and git-tag) to specify a commit/tag message\nwithout invoking the editor. This patch teaches \"git notes edit\"\nto respect the same options for specifying a notes message without\ninvoking the editor.\n\nMultiple \"-m\" and/or \"-F\" options are concatenated as separate\nparagraphs.\n\nThe patch also updates the \"git notes\" documentation and adds\nselftests for the new functionality. Unfortunately, the added\nselftests include a couple of lines with trailing whitespace\n(without these the test will fail). This may cause git to warn\nabout \"whitespace errors\".\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/git-notes.txt |   16 ++++++++++-\n git-notes.sh                |   64 +++++++++++++++++++++++++++++++++++++-----\n t/t3301-notes.sh            |   35 +++++++++++++++++++++++\n 3 files changed, 106 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex 7136016..94cceb1 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -8,7 +8,7 @@ git-notes - Add/inspect commit notes\n SYNOPSIS\n --------\n [verse]\n-'git-notes' (edit | show) [commit]\n+'git-notes' (edit [-F <file> | -m <msg>] | show) [commit]\n \n DESCRIPTION\n -----------\n@@ -33,6 +33,20 @@ show::\n \tShow the notes for a given commit (defaults to HEAD).\n \n \n+OPTIONS\n+-------\n+-m <msg>::\n+\tUse the given note message (instead of prompting).\n+\tIf multiple `-m` (or `-F`) options are given, their\n+\tvalues are concatenated as separate paragraphs.\n+\n+-F <file>::\n+\tTake the note message from the given file.  Use '-' to\n+\tread the note message from the standard input.\n+\tIf multiple `-F` (or `-m`) options are given, their\n+\tvalues are concatenated as separate paragraphs.\n+\n+\n Author\n ------\n Written by Johannes Schindelin <johannes.schindelin@gmx.de>\ndiff --git a/git-notes.sh b/git-notes.sh\nindex f06c254..e642e47 100755\n--- a/git-notes.sh\n+++ b/git-notes.sh\n@@ -1,16 +1,59 @@\n #!/bin/sh\n \n-USAGE=\"(edit | show) [commit]\"\n+USAGE=\"(edit [-F <file> | -m <msg>] | show) [commit]\"\n . git-sh-setup\n \n-test -n \"$3\" && usage\n-\n test -z \"$1\" && usage\n ACTION=\"$1\"; shift\n \n test -z \"$GIT_NOTES_REF\" && GIT_NOTES_REF=\"$(git config core.notesref)\"\n test -z \"$GIT_NOTES_REF\" && GIT_NOTES_REF=\"refs/notes/commits\"\n \n+MESSAGE=\n+while test $# != 0\n+do\n+\tcase \"$1\" in\n+\t-m)\n+\t\ttest \"$ACTION\" = \"edit\" || usage\n+\t\tshift\n+\t\tif test \"$#\" = \"0\"; then\n+\t\t\tdie \"error: option -m needs an argument\"\n+\t\telse\n+\t\t\tif [ -z \"$MESSAGE\" ]; then\n+\t\t\t\tMESSAGE=\"$1\"\n+\t\t\telse\n+\t\t\t\tMESSAGE=\"$MESSAGE\n+\n+$1\"\n+\t\t\tfi\n+\t\t\tshift\n+\t\tfi\n+\t\t;;\n+\t-F)\n+\t\ttest \"$ACTION\" = \"edit\" || usage\n+\t\tshift\n+\t\tif test \"$#\" = \"0\"; then\n+\t\t\tdie \"error: option -F needs an argument\"\n+\t\telse\n+\t\t\tif [ -z \"$MESSAGE\" ]; then\n+\t\t\t\tMESSAGE=\"$(cat \"$1\")\"\n+\t\t\telse\n+\t\t\t\tMESSAGE=\"$MESSAGE\n+\n+$(cat \"$1\")\"\n+\t\t\tfi\n+\t\t\tshift\n+\t\tfi\n+\t\t;;\n+\t-*)\n+\t\tusage\n+\t\t;;\n+\t*)\n+\t\tbreak\n+\t\t;;\n+\tesac\n+done\n+\n COMMIT=$(git rev-parse --verify --default HEAD \"$@\") ||\n die \"Invalid commit: $@\"\n \n@@ -29,19 +72,24 @@ edit)\n \t\ttest -f \"$GIT_INDEX_FILE\" && rm \"$GIT_INDEX_FILE\"\n \t' 0\n \n-\tGIT_NOTES_REF= git log -1 $COMMIT | sed \"s/^/#/\" > \"$MSG_FILE\"\n-\n \tCURRENT_HEAD=$(git show-ref \"$GIT_NOTES_REF\" | cut -f 1 -d ' ')\n \tif [ -z \"$CURRENT_HEAD\" ]; then\n \t\tPARENT=\n \telse\n \t\tPARENT=\"-p $CURRENT_HEAD\"\n \t\tgit read-tree \"$GIT_NOTES_REF\" || die \"Could not read index\"\n-\t\tgit cat-file blob :$COMMIT >> \"$MSG_FILE\" 2> /dev/null\n \tfi\n \n-\tcore_editor=\"$(git config core.editor)\"\n-\t${GIT_EDITOR:-${core_editor:-${VISUAL:-${EDITOR:-vi}}}} \"$MSG_FILE\"\n+\tif [ -z \"$MESSAGE\" ]; then\n+\t\tGIT_NOTES_REF= git log -1 $COMMIT | sed \"s/^/#/\" > \"$MSG_FILE\"\n+\t\tif [ ! -z \"$CURRENT_HEAD\" ]; then\n+\t\t\tgit cat-file blob :$COMMIT >> \"$MSG_FILE\" 2> /dev/null\n+\t\tfi\n+\t\tcore_editor=\"$(git config core.editor)\"\n+\t\t${GIT_EDITOR:-${core_editor:-${VISUAL:-${EDITOR:-vi}}}} \"$MSG_FILE\"\n+\telse\n+\t\techo \"$MESSAGE\" > \"$MSG_FILE\"\n+\tfi\n \n \tgrep -v ^# < \"$MSG_FILE\" | git stripspace > \"$MSG_FILE\".processed\n \tmv \"$MSG_FILE\".processed \"$MSG_FILE\"\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex 73e53be..9eaa338 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -110,5 +110,40 @@ test_expect_success 'show multi-line notes' '\n \tgit log -2 > output &&\n \ttest_cmp expect-multiline output\n '\n+test_expect_success 'create -m and -F notes (setup)' '\n+\t: > a4 &&\n+\tgit add a4 &&\n+\ttest_tick &&\n+\tgit commit -m 4th &&\n+\techo \"xyzzy\" > note5 &&\n+\tgit notes edit -m spam -F note5 -m \"foo\n+bar\n+baz\"\n+'\n+\n+cat > expect-m-and-F << EOF\n+commit 15023535574ded8b1a89052b32673f84cf9582b8\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:16:13 2005 -0700\n+\n+    4th\n+\n+Notes:\n+    spam\n+    \n+    xyzzy\n+    \n+    foo\n+    bar\n+    baz\n+EOF\n+\n+printf \"\\n\" >> expect-m-and-F\n+cat expect-multiline >> expect-m-and-F\n+\n+test_expect_success 'show -m and -F notes' '\n+\tgit log -3 > output &&\n+\ttest_cmp expect-m-and-F output\n+'\n \n test_done\n-- \n1.6.3.rc0.1.gf800\n"},{"id":"114101","messageId":"4A0ED68F.6060200@drmicha.warpmail.net","threadId":"19365","inReplyTo":"cover.1242437696.git.johan@herland.net","subject":"Re: [PATCH 0/5] RESEND: git notes","fromName":"","fromEmail":"git@drmicha.warpmail.net","sentAt":"2009-05-16T15:06:55Z","receivedAt":"2009-05-16T15:06:55Z","isPatch":true,"sender":{"key":"git@grubix.eu","avatar":"https://avatars.githubusercontent.com/u/233215?v=4"},"body":"Johan Herland venit, vidit, dixit 16.05.2009 03:45:\n> Hi,\n> \n> Dscho has asked me to take over the responsibility for the js/notes\n> patch series.\n> \n> The following is a re-roll and resend of the patch series currently\n> in pu, plus my own 2 patches for adding support for \"-m\" and \"-F\" to\n> \"git notes edit\".\n> \n> On advice from Dscho, I have squashed the current bugfix and cleanup\n> patches in js/notes into the first 4 \"main\" patches. As a result the\n> original 15 + 2 patch series is now down to 5 (4 + 1) patches.\n> \n> In sum, these 5 patches produce the exact same result as the original\n> js/notes series (plus my 2 patches).\n> \n> I have taken the liberty of squashing the various Signed-off-by tags\n> (along with their corresponding patches) into these 5 new patches.\n> I hope this is OK with everybody. If not, I apologize; please tell me,\n> and I will re-send.\n\nWell, effectively you removed me (and others) from the author list :|\n\nI think the issue with the tree difference after this series (compared\nto the original one) shows that this squashing action makes reviewers'\nlives more complicated rather than easier. If it were the other way\nround squashing would be fine, of course.\n\nMichael\n\n> \n> \n> Have fun! :)\n> \n> ...Johan\n> \n> \n> Johan Herland (1):\n>   Teach \"-m <msg>\" and \"-F <file>\" to \"git notes edit\"\n> \n> Johannes Schindelin (4):\n>   Introduce commit notes\n>   Add a script to edit/inspect notes\n>   Speed up git notes lookup\n>   Add an expensive test for git-notes\n> \n>  .gitignore                       |    1 +\n>  Documentation/config.txt         |   13 +++\n>  Documentation/git-notes.txt      |   60 ++++++++++++++\n>  Makefile                         |    3 +\n>  cache.h                          |    4 +\n>  command-list.txt                 |    1 +\n>  commit.c                         |    1 +\n>  config.c                         |    5 +\n>  environment.c                    |    1 +\n>  git-notes.sh                     |  121 ++++++++++++++++++++++++++++\n>  notes.c                          |  160 ++++++++++++++++++++++++++++++++++++++\n>  notes.h                          |    7 ++\n>  pretty.c                         |    5 +\n>  t/t3301-notes.sh                 |  149 +++++++++++++++++++++++++++++++++++\n>  t/t3302-notes-index-expensive.sh |   98 +++++++++++++++++++++++\n>  15 files changed, 629 insertions(+), 0 deletions(-)\n>  create mode 100644 Documentation/git-notes.txt\n>  create mode 100755 git-notes.sh\n>  create mode 100644 notes.c\n>  create mode 100644 notes.h\n>  create mode 100755 t/t3301-notes.sh\n>  create mode 100755 t/t3302-notes-index-expensive.sh\n> \n"},{"id":"114102","messageId":"200905161801.33369.johan@herland.net","threadId":"19365","inReplyTo":"4A0ED68F.6060200@drmicha.warpmail.net","subject":"Re: [PATCH 0/5] RESEND: git notes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-05-16T16:01:33Z","receivedAt":"2009-05-16T16:01:33Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Saturday 16 May 2009, git@drmicha.warpmail.net wrote:\n> Johan Herland venit, vidit, dixit 16.05.2009 03:45:\n> > Dscho has asked me to take over the responsibility for the js/notes\n> > patch series.\n> >\n> > The following is a re-roll and resend of the patch series currently\n> > in pu, plus my own 2 patches for adding support for \"-m\" and \"-F\" to\n> > \"git notes edit\".\n> >\n> > On advice from Dscho, I have squashed the current bugfix and cleanup\n> > patches in js/notes into the first 4 \"main\" patches. As a result the\n> > original 15 + 2 patch series is now down to 5 (4 + 1) patches.\n> >\n> > In sum, these 5 patches produce the exact same result as the original\n> > js/notes series (plus my 2 patches).\n> >\n> > I have taken the liberty of squashing the various Signed-off-by tags\n> > (along with their corresponding patches) into these 5 new patches.\n> > I hope this is OK with everybody. If not, I apologize; please tell me,\n> > and I will re-send.\n>\n> Well, effectively you removed me (and others) from the author list :|\n\nI know, and I'm sorry. This is not an ideal solution.\n\nWhen a patch series is cooking in 'pu' and it receives fixes and cleanups \nfrom people on the list, traditionally (I believe) Junio expects a \nnew/replacement series that incorporates this feedback to be posted to the \nlist. This results in cleaner series, but at the cost of collapsing \nauthorship down to the primary author.\n\nIn this case, the original author (Dscho) has not had the time to do this \nre-roll, and the bugfixes/cleanups have instead been added to the end of the \npatch series, resulting in the original js/notes series growing from 4 to 15 \npatches.\n\nI believe the re-roll I have done results in a nicer and cleaner overall \nseries, and I have tried to mitigate the loss of authorship by adding small \nnotes in the commit messages mentioning the fixes contributed by others. \nStill, Dscho stands as the primary author for the 4 \"main\" patches, and I \nbelieve this is correct, even if the notes feature has been discussed and \ndeveloped conceptually by many others (myself included).\n\n> I think the issue with the tree difference after this series (compared\n> to the original one) shows that this squashing action makes reviewers'\n> lives more complicated rather than easier. If it were the other way\n> round squashing would be fine, of course.\n\nWhen taking over this patch series, I believe I had 4 choices:\n\n1. Leave everything as-is, adding my two patches on top of the existing 15. \nFor reviewers of the notes feature as a whole, I don't believe an ever-\ngrowing patch series makes their life easier. Also, I believe a shorter \npatch series looks nicer after being merged to 'next' (otherwise, why are we \ndoing this whole 're-rolling while in pu'-workflow?)\n\n2. Squash the existing 15-patch series down to 4 patches, and add my 2 \npatches on top. This would yield a 4+2-patch series that would by tree-\nidentical to the original js/notes series after patch #4. However, \neverybody's authorship would be lost, except my own (patch 5 & 6), and this \nstrikes me as a bit hypocritical.\n\n3. Like #2, but also squash my first patch (the bugfix/cleanup part) into \nthe first 4 patches. This, I believe, best follows the intent of 'pu' \n(squashing bugfixes and cleanups into the original series). Unfortunately, \nit breaks the tree-equality provided in #2.\n\n4. Squash all 17 patches into a 4-patch series. However, I believe the final \npatch (adding support for '-m' and '-F' to 'git notes edit') is more of a \nfeature patch, than a bugfix/cleanup patch, and thus should not be squashed.\n\n\nI therefore went with #3, which is - as I said above - not an ideal \nsolution, but AFAICS the best under the circumstances.\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"114137","messageId":"alpine.DEB.1.00.0905171826200.26154@pacific.mpi-cbg.de","threadId":"19365","inReplyTo":"4A0ED68F.6060200@drmicha.warpmail.net","subject":"Re: [PATCH 0/5] RESEND: git notes","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-05-17T16:31:15Z","receivedAt":"2009-05-17T16:31:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 16 May 2009, git@drmicha.warpmail.net wrote:\n\n> Johan Herland venit, vidit, dixit 16.05.2009 03:45:\n> \n> > Dscho has asked me to take over the responsibility for the js/notes \n> > patch series.\n> > \n> > The following is a re-roll and resend of the patch series currently in \n> > pu, plus my own 2 patches for adding support for \"-m\" and \"-F\" to \"git \n> > notes edit\".\n> > \n> > On advice from Dscho, I have squashed the current bugfix and cleanup \n> > patches in js/notes into the first 4 \"main\" patches. As a result the \n> > original 15 + 2 patch series is now down to 5 (4 + 1) patches.\n> > \n> > In sum, these 5 patches produce the exact same result as the original\n> > js/notes series (plus my 2 patches).\n\nActually, I would like this series not to go in as-is, as the most \nimportant obstacle for inclusion into 1.6.2 (IIRC) was that performance \nwas pretty lousy.\n\nIn the least, it should allow reading fan-out directories, but preferably \nit would even _write_ fan-out directories.\n\nThe only tricky part was how to decide which notes to take if there are \nambiguities, but I think a concatenation (or that shallower paths take \nprecedence) are both viable strategies.\n\n> Well, effectively you removed me (and others) from the author list :|\n\nFWIW whenever I squashed other people's patches into mine (and that is not \nlimited to the 'notes' branch), I tried to add proper attribution to the \ncommit message.  In Open Source, the currency is merit, so attribution is \nimportant.\n\nCiao,\nDscho\n"},{"id":"114333","messageId":"alpine.DEB.1.00.0905201224080.16461@intel-tinevez-2-302","threadId":"19365","inReplyTo":"cover.1242473357.git.johan@herland.net","subject":"Re: [PATCHv2 0/5] RESEND: git notes","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-05-20T10:25:20Z","receivedAt":"2009-05-20T10:25:20Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 16 May 2009, Johan Herland wrote:\n\n> Dscho has asked me to take over the responsibility for the js/notes \n> patch series.\n\nHow's the enhanced note-tree lookup going?\n\nAnd do you have updated the patches to attribute people who provided \nnow-squashed-in patches properly?\n\nCiao,\nDscho\n"},{"id":"114340","messageId":"200905201354.53501.johan@herland.net","threadId":"19365","inReplyTo":"alpine.DEB.1.00.0905201224080.16461@intel-tinevez-2-302","subject":"Re: [PATCHv2 0/5] RESEND: git notes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-05-20T11:54:53Z","receivedAt":"2009-05-20T11:54:53Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Wednesday 20 May 2009, Johannes Schindelin wrote:\n> How's the enhanced note-tree lookup going?\n\nHaven't had time to work on it, yet.\n\n> And do you have updated the patches to attribute people who provided\n> now-squashed-in patches properly?\n\nYes, I have re-rolled the commit message to include the full name (not \njust the initials) of each contributor. Will resend when time permits. \nIs there anything more I can do to provide proper attribution?\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"}]}