{"thread":{"id":"22744","subject":"[RFC PATCH v3 05/12] rebase -i: invoke post-rewrite hook","startedAt":"2010-02-20T22:16:21Z","lastAt":"2010-03-11T10:59:24Z","messageCount":66,"participants":["Thomas Rast","Junio C Hamano","Stephen Boyd","Johan Herland"],"isPatch":true,"patchVersion":3,"patchTotal":12},"messages":[{"id":"135194","messageId":"cover.1266703765.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266361759.git.trast@student.ethz.ch","subject":"[RFC PATCH v3 00/12] several notes refs, post-rewrite, notes rewriting","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-20T22:16:21Z","receivedAt":"2010-02-20T22:16:21Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"> I'm running out of git time today\n\nI should repeat that incantation more often, it mysteriously freed up\nmore time.\n\nBrief overview of changes since v2, with some inline questions about\nthings that need to be settled:\n\n* new [1/12] \"Support showing notes from more than one notes tree\" that\n  teaches a \"notes.displayRef\" option which can glob for notes that\n  should be shown, and can be specified more than once.\n\n  - maybe we should default this to \"refs/notes/*\"?\n\n  - currently git-notes and the configs don't agree on what is a valid\n    notes ref.  git-notes refuses even *showing* outside of\n    refs/notes/.  I tend towards changing git-notes at least as far as\n    reading notes is concerned (show/list), so that remote notes work.\n\n* dropped [5/11] \"notes: clean up t3301\" as per Junio's comments\n\n* changed [7/12] \"notes: implement helpers needed for note copying\n  during rewrite\" to support \"notes.rewriteRef\" as announced earlier.\n  \"notes.rewrite.<cmd>\" now default to true, so that you only need to\n  enable one, namely set notes.rewriteRef = refs/notes/*.\n\n  - do we want a default?\n\n  - do we want the rewriteRefs to also implicitly be displayRefs?\n\n  - the \"notes.rewriteMode = overwrite for notes/volatile/*\" hinted at\n    earlier is missing.  Does anyone need that?\n\n* new [12/12] \"notes: add shorthand --ref to override GIT_NOTES_REF\"\n\n\nI haven't changed [10/12] & [11/12] \"filter-branch...\" yet, as I'm not\nreally sure what the best format of such a filter would be.\n\nThe existing code is probably a neat way of editing the notes, but it\ncreates a new commit to the notes ref for every rewritten commit.  If\nI change the default --notes-filter to cope with multi-rewriting, this\nmeans a new notes commit per rewritten commit.  Seems rather wasteful\njust to salvage your notes across an otherwise unrelated\nfilter-branch.\n\nSo the obvious fix would seem to be a --post-rewrite addition that\njust pipes through 'git notes copy --for-rewrite=filter-branch', but\nthat's again not very customizable short of reinventing the whole\nthing.  Any ideas?\n\n\nThomas Rast (12):\n  Support showing notes from more than one notes tree\n  Documentation: document post-rewrite hook\n  commit --amend: invoke post-rewrite hook\n  rebase: invoke post-rewrite hook\n  rebase -i: invoke post-rewrite hook\n  notes: implement 'git notes copy --stdin'\n  notes: implement helpers needed for note copying during rewrite\n  rebase: support automatic notes copying\n  commit --amend: copy notes to the new commit\n  filter-branch: invoke post-rewrite hook\n  filter-branch: learn how to filter notes\n  notes: add shorthand --ref to override GIT_NOTES_REF\n\n Documentation/config.txt            |   40 +++++++-\n Documentation/git-filter-branch.txt |   20 +++-\n Documentation/git-notes.txt         |   20 +++-\n Documentation/githooks.txt          |   40 +++++++\n builtin-commit.c                    |   49 ++++++++\n builtin-notes.c                     |  168 +++++++++++++++++++++++++++-\n builtin.h                           |   16 +++\n git-am.sh                           |   13 ++\n git-filter-branch.sh                |   37 ++++++\n git-rebase--interactive.sh          |   45 +++++++-\n git-rebase.sh                       |    6 +\n notes.c                             |  125 +++++++++++++++++++-\n notes.h                             |   16 +++\n pretty.c                            |    6 +-\n refs.c                              |    4 +-\n refs.h                              |    5 +\n t/t3301-notes.sh                    |  213 ++++++++++++++++++++++++++++++++--\n t/t3306-notes-prune.sh              |    6 +-\n t/t3400-rebase.sh                   |   17 +++\n t/t3404-rebase-interactive.sh       |   11 ++\n t/t5407-post-rewrite-hook.sh        |  188 ++++++++++++++++++++++++++++++\n t/t7003-filter-branch.sh            |   22 ++++\n t/t7501-commit.sh                   |   12 ++\n 23 files changed, 1046 insertions(+), 33 deletions(-)\n create mode 100755 t/t5407-post-rewrite-hook.sh\n"},{"id":"135199","messageId":"34f0e6d201f6bddfc8f4f374493b1b54d60b82cd.1266703765.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266703765.git.trast@student.ethz.ch","subject":"[RFC PATCH v3 01/12] Support showing notes from more than one notes tree","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-20T22:16:22Z","receivedAt":"2010-02-20T22:16:22Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"With this patch, you can set notes.displayRef to a glob that points at\nyour favourite notes refs, e.g.,\n\n[notes]\n\tdisplayRef = refs/notes/*\n\nThen git-log and friends will show notes from all trees.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n Documentation/config.txt |   13 +++++-\n notes.c                  |  104 +++++++++++++++++++++++++++++++++++++++++++---\n notes.h                  |    7 +++\n pretty.c                 |    6 +-\n refs.c                   |    4 +-\n refs.h                   |    5 ++\n t/t3301-notes.sh         |   65 +++++++++++++++++++++++------\n t/t3306-notes-prune.sh   |    6 +-\n 8 files changed, 181 insertions(+), 29 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 4c36aa9..3c5cfec 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -517,7 +517,7 @@ core.notesRef::\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+appended to the commit message, separated by a \"Notes from <refname>:\" 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@@ -1300,6 +1300,17 @@ mergetool.keepTemporaries::\n mergetool.prompt::\n \tPrompt before each invocation of the merge resolution program.\n \n+notes.displayRef::\n+\tWhen showing commit messages, also show notes which are stored\n+\tin the given ref.  The ref may also be a glob, in which case\n+\tnotes from all matching refs will be shown.  You may also\n+\tspecify this configuration variable several times.  If a ref\n+\tdoes not exist, it will be skipped silently.\n++\n+The effective value of \"core.notesRef\" (possibly overridden by\n+GIT_NOTES_REF) is also implicitly added to the list of refs to be\n+displayed.\n+\n pack.window::\n \tThe size of the window used by linkgit:git-pack-objects[1] when no\n \twindow size is given on the command line. Defaults to 10.\ndiff --git a/notes.c b/notes.c\nindex 3ba3e6d..d37178b 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -5,6 +5,8 @@\n #include \"utf8.h\"\n #include \"strbuf.h\"\n #include \"tree-walk.h\"\n+#include \"string-list.h\"\n+#include \"refs.h\"\n \n /*\n  * Use a non-balancing simple 16-tree structure with struct int_node as\n@@ -68,6 +70,9 @@ struct non_note {\n \n struct notes_tree default_notes_tree;\n \n+struct string_list display_notes_refs;\n+struct notes_tree **display_notes_trees;\n+\n static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\tstruct int_node *node, unsigned int n);\n \n@@ -828,6 +833,44 @@ int combine_notes_ignore(unsigned char *cur_sha1,\n \treturn 0;\n }\n \n+static int string_list_add_one_ref(const char *path, const unsigned char *sha1,\n+\t\t\t\t   int flag, void *cb)\n+{\n+\tstruct string_list *refs = cb;\n+\tstring_list_insert(path, refs);\n+\treturn 0;\n+}\n+\n+void string_list_add_refs_by_glob(struct string_list *list, const char *glob)\n+{\n+\tif (has_glob_specials(glob))\n+\t\tfor_each_glob_ref(string_list_add_one_ref, glob, list);\n+\telse\n+\t\tstring_list_insert(glob, list);\n+}\n+\n+static int notes_display_config(const char *k, const char *v, void *cb)\n+{\n+\tif (!strcmp(k, \"notes.displayref\")) {\n+\t\tstring_list_add_refs_by_glob(&display_notes_refs, v);\n+\t\treturn 0;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static const char *default_notes_ref()\n+{\n+\tconst char *notes_ref = NULL;\n+\tif (!notes_ref)\n+\t\tnotes_ref = getenv(GIT_NOTES_REF_ENVIRONMENT);\n+\tif (!notes_ref)\n+\t\tnotes_ref = notes_ref_name; /* value of core.notesRef config */\n+\tif (!notes_ref)\n+\t\tnotes_ref = GIT_NOTES_DEFAULT_REF;\n+\treturn notes_ref;\n+}\n+\n void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\tcombine_notes_fn combine_notes, int flags)\n {\n@@ -840,11 +883,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \tassert(!t->initialized);\n \n \tif (!notes_ref)\n-\t\tnotes_ref = getenv(GIT_NOTES_REF_ENVIRONMENT);\n-\tif (!notes_ref)\n-\t\tnotes_ref = notes_ref_name; /* value of core.notesRef config */\n-\tif (!notes_ref)\n-\t\tnotes_ref = GIT_NOTES_DEFAULT_REF;\n+\t\tnotes_ref = default_notes_ref();\n \n \tif (!combine_notes)\n \t\tcombine_notes = combine_notes_concatenate;\n@@ -868,6 +907,43 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \tload_subtree(t, &root_tree, t->root, 0);\n }\n \n+struct load_notes_cb_data\n+{\n+\tint counter;\n+\tstruct notes_tree **trees;\n+};\n+\n+static int load_one_display_note_ref(struct string_list_item *item,\n+\t\t\t\t     void *cb_data)\n+{\n+\tstruct load_notes_cb_data *c = cb_data;\n+\tstruct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));\n+\tinit_notes(t, item->string, combine_notes_ignore, 0);\n+\tc->trees[c->counter++] = t;\n+\treturn 0;\n+}\n+\n+struct notes_tree **load_notes_trees(struct string_list *refs)\n+{\n+\tstruct notes_tree **trees;\n+\tstruct load_notes_cb_data cb_data;\n+\ttrees = xmalloc((display_notes_refs.nr+1) * sizeof(struct notes_tree *));\n+\tcb_data.counter = 0;\n+\tcb_data.trees = trees;\n+\tfor_each_string_list(load_one_display_note_ref, refs, &cb_data);\n+\ttrees[cb_data.counter] = NULL;\n+\treturn trees;\n+}\n+\n+void init_display_notes()\n+{\n+\tdisplay_notes_refs.strdup_strings = 1;\n+\tstring_list_insert(default_notes_ref(), &display_notes_refs);\n+\tgit_config(notes_display_config, NULL);\n+\tdisplay_notes_trees = load_notes_trees(&display_notes_refs);\n+\tstring_list_clear(&display_notes_refs, 0);\n+}\n+\n void add_note(struct notes_tree *t, const unsigned char *object_sha1,\n \t\tconst unsigned char *note_sha1, combine_notes_fn combine_notes)\n {\n@@ -1016,7 +1092,12 @@ void format_note(struct notes_tree *t, const unsigned char *object_sha1,\n \tif (msglen && msg[msglen - 1] == '\\n')\n \t\tmsglen--;\n \n-\tif (flags & NOTES_SHOW_HEADER)\n+\tif (flags & NOTES_SHOW_HEADER_WITH_REF && t->ref) {\n+\t\tconst char *ref = t->ref;\n+\t\tif (!prefixcmp(ref, \"refs/\"))\n+\t\t\tref += 5;\n+\t\tstrbuf_addf(sb, \"\\nNotes from %s:\\n\", ref);\n+\t} else if (flags & (NOTES_SHOW_HEADER|NOTES_SHOW_HEADER_WITH_REF))\n \t\tstrbuf_addstr(sb, \"\\nNotes:\\n\");\n \n \tfor (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {\n@@ -1030,3 +1111,14 @@ void format_note(struct notes_tree *t, const unsigned char *object_sha1,\n \n \tfree(msg);\n }\n+\n+void format_display_notes(const unsigned char *object_sha1,\n+\t\t\t  struct strbuf *sb, const char *output_encoding, int flags)\n+{\n+\tint i;\n+\tif (!display_notes_trees)\n+\t\tinit_display_notes();\n+\tfor (i = 0; display_notes_trees[i]; i++)\n+\t\tformat_note(display_notes_trees[i], object_sha1, sb,\n+\t\t\t    output_encoding, flags);\n+}\ndiff --git a/notes.h b/notes.h\nindex bad03cc..32ae2f0 100644\n--- a/notes.h\n+++ b/notes.h\n@@ -184,6 +184,7 @@ int for_each_note(struct notes_tree *t, int flags, each_note_fn fn,\n /* Flags controlling how notes are formatted */\n #define NOTES_SHOW_HEADER 1\n #define NOTES_INDENT 2\n+#define NOTES_SHOW_HEADER_WITH_REF 4\n \n /*\n  * Fill the given strbuf with the notes associated with the given object.\n@@ -198,4 +199,10 @@ int for_each_note(struct notes_tree *t, int flags, each_note_fn fn,\n void format_note(struct notes_tree *t, const unsigned char *object_sha1,\n \t\tstruct strbuf *sb, const char *output_encoding, int flags);\n \n+void format_display_notes(const unsigned char *object_sha1,\n+\t\t\t  struct strbuf *sb, const char *output_encoding, int flags);\n+\n+struct notes_tree **load_notes_trees(struct string_list *refs);\n+void string_list_add_refs_by_glob(struct string_list *list, const char *glob);\n+\n #endif\ndiff --git a/pretty.c b/pretty.c\nindex f999485..c185fd2 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -775,7 +775,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\t}\n \t\treturn 0;\t/* unknown %g placeholder */\n \tcase 'N':\n-\t\tformat_note(NULL, commit->object.sha1, sb,\n+\t\tformat_display_notes(commit->object.sha1, sb,\n \t\t\t    git_log_output_encoding ? git_log_output_encoding\n \t\t\t\t\t\t    : git_commit_encoding, 0);\n \t\treturn 1;\n@@ -1096,8 +1096,8 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,\n \t\tstrbuf_addch(sb, '\\n');\n \n \tif (context->show_notes)\n-\t\tformat_note(NULL, commit->object.sha1, sb, encoding,\n-\t\t\t    NOTES_SHOW_HEADER | NOTES_INDENT);\n+\t\tformat_display_notes(commit->object.sha1, sb, encoding,\n+\t\t\t\t     NOTES_SHOW_HEADER_WITH_REF | NOTES_INDENT);\n \n \tfree(reencoded);\n }\ndiff --git a/refs.c b/refs.c\nindex 503a8c2..6634091 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -695,7 +695,6 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,\n {\n \tstruct strbuf real_pattern = STRBUF_INIT;\n \tstruct ref_filter filter;\n-\tconst char *has_glob_specials;\n \tint ret;\n \n \tif (!prefix && prefixcmp(pattern, \"refs/\"))\n@@ -704,8 +703,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,\n \t\tstrbuf_addstr(&real_pattern, prefix);\n \tstrbuf_addstr(&real_pattern, pattern);\n \n-\thas_glob_specials = strpbrk(pattern, \"?*[\");\n-\tif (!has_glob_specials) {\n+\tif (!has_glob_specials(pattern)) {\n \t\t/* Append impiled '/' '*' if not present. */\n \t\tif (real_pattern.buf[real_pattern.len - 1] != '/')\n \t\t\tstrbuf_addch(&real_pattern, '/');\ndiff --git a/refs.h b/refs.h\nindex f7648b9..4a18b08 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -28,6 +28,11 @@ struct ref_lock {\n extern int for_each_glob_ref(each_ref_fn, const char *pattern, void *);\n extern int for_each_glob_ref_in(each_ref_fn, const char *pattern, const char* prefix, void *);\n \n+static inline const char *has_glob_specials(const char *pattern)\n+{\n+\treturn strpbrk(pattern, \"?*[\");\n+}\n+\n /* can be used to learn about broken ref and symref */\n extern int for_each_rawref(each_ref_fn, void *);\n \ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex 90178f9..0dfff82 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -98,7 +98,7 @@ Date:   Thu Apr 7 15:14:13 2005 -0700\n \n     2nd\n \n-Notes:\n+Notes from notes/commits:\n     b1\n EOF\n \n@@ -125,7 +125,7 @@ Date:   Thu Apr 7 15:15:13 2005 -0700\n \n     3rd\n \n-Notes:\n+Notes from notes/commits:\n     b3\n     c3c3c3c3\n     d3d3d3\n@@ -154,7 +154,7 @@ Date:   Thu Apr 7 15:16:13 2005 -0700\n \n     4th\n \n-Notes:\n+Notes from notes/commits:\n     xyzzy\n EOF\n \n@@ -182,7 +182,7 @@ test_expect_success 'git log --pretty=raw does not show notes' '\n \n cat >>expect <<EOF\n \n-Notes:\n+Notes from notes/commits:\n     xyzzy\n EOF\n test_expect_success 'git log --show-notes' '\n@@ -237,7 +237,7 @@ Date:   Thu Apr 7 15:17:13 2005 -0700\n \n     5th\n \n-Notes:\n+Notes from notes/commits:\n     spam\n $whitespace\n     foo\n@@ -415,7 +415,7 @@ Date:   Thu Apr 7 15:18:13 2005 -0700\n \n     6th\n \n-Notes:\n+Notes from notes/other:\n     other note\n EOF\n \n@@ -448,7 +448,46 @@ test_expect_success 'Do not show note when core.notesRef is overridden' '\n \ttest_cmp expect-not-other output\n '\n \n+cat > expect-both << EOF\n+commit 387a89921c73d7ed72cd94d179c1c7048ca47756\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:18:13 2005 -0700\n+\n+    6th\n+\n+Notes from notes/other:\n+    other note\n+\n+commit bd1753200303d0a0344be813e504253b3d98e74d\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:17:13 2005 -0700\n+\n+    5th\n+EOF\n+\n+test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '\n+\tgit config --unset core.notesRef &&\n+\tgit config notes.displayRef \"refs/notes/*\" &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect-both output\n+'\n+\n+test_expect_success 'core.notesRef is implicitly in notes.displayRef' '\n+\tgit config core.notesRef refs/notes/other &&\n+\tgit config notes.displayRef refs/notes/commits &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect-both output\n+'\n+\n+test_expect_success 'notes.displayRef can be given more than once' '\n+\tgit config --unset core.notesRef &&\n+\tgit config --add notes.displayRef refs/notes/other &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect-both output\n+'\n+\n test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '\n+\tgit config core.notesRef refs/notes/other &&\n \techo \"Note on a tree\" > expect\n \tgit notes add -m \"Note on a tree\" HEAD: &&\n \tgit notes show HEAD: > actual &&\n@@ -472,7 +511,7 @@ Date:   Thu Apr 7 15:19:13 2005 -0700\n \n     7th\n \n-Notes:\n+Notes from notes/other:\n     other note\n EOF\n \n@@ -503,7 +542,7 @@ Date:   Thu Apr 7 15:21:13 2005 -0700\n \n     9th\n \n-Notes:\n+Notes from notes/other:\n     yet another note\n EOF\n \n@@ -533,7 +572,7 @@ Date:   Thu Apr 7 15:21:13 2005 -0700\n \n     9th\n \n-Notes:\n+Notes from notes/other:\n     yet another note\n $whitespace\n     yet another note\n@@ -552,7 +591,7 @@ Date:   Thu Apr 7 15:22:13 2005 -0700\n \n     10th\n \n-Notes:\n+Notes from notes/other:\n     other note\n EOF\n \n@@ -569,7 +608,7 @@ Date:   Thu Apr 7 15:22:13 2005 -0700\n \n     10th\n \n-Notes:\n+Notes from notes/other:\n     other note\n $whitespace\n     yet another note\n@@ -588,7 +627,7 @@ Date:   Thu Apr 7 15:23:13 2005 -0700\n \n     11th\n \n-Notes:\n+Notes from notes/other:\n     other note\n $whitespace\n     yet another note\n@@ -619,7 +658,7 @@ Date:   Thu Apr 7 15:23:13 2005 -0700\n \n     11th\n \n-Notes:\n+Notes from notes/other:\n     yet another note\n $whitespace\n     yet another note\ndiff --git a/t/t3306-notes-prune.sh b/t/t3306-notes-prune.sh\nindex a0ed035..24dd503 100755\n--- a/t/t3306-notes-prune.sh\n+++ b/t/t3306-notes-prune.sh\n@@ -30,7 +30,7 @@ Date:   Thu Apr 7 15:15:13 2005 -0700\n \n     3rd\n \n-Notes:\n+Notes from notes/commits:\n     Note #3\n \n commit 08341ad9e94faa089d60fd3f523affb25c6da189\n@@ -39,7 +39,7 @@ Date:   Thu Apr 7 15:14:13 2005 -0700\n \n     2nd\n \n-Notes:\n+Notes from notes/commits:\n     Note #2\n \n commit ab5f302035f2e7aaf04265f08b42034c23256e1f\n@@ -48,7 +48,7 @@ Date:   Thu Apr 7 15:13:13 2005 -0700\n \n     1st\n \n-Notes:\n+Notes from notes/commits:\n     Note #1\n END_OF_LOG\n \n-- \n1.7.0.59.g783f8\n"},{"id":"135193","messageId":"a58dc7185725f009447910137827a1a2949de510.1266703765.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266703765.git.trast@student.ethz.ch","subject":"[RFC PATCH v3 02/12] Documentation: document post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-20T22:16:23Z","receivedAt":"2010-02-20T22:16:23Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"This defines the behaviour of the post-rewrite hook support, which\nwill be implemented in the following patches.\n\nWe deliberately do not document how often the hook will be invoked per\nrewriting command, but the interface is designed to keep that at\n\"once\".  This would currently not matter too much, since both rebase\nand filter-branch are shellscripts and spawn many processes anyway.\nHowever, when a fast sequencer in C is implemented, it will be\nbeneficial to only have to run the hook once.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n Documentation/githooks.txt |   36 ++++++++++++++++++++++++++++++++++++\n 1 files changed, 36 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex 87e2c03..41895e9 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -317,6 +317,42 @@ This hook is invoked by 'git gc --auto'. It takes no parameter, and\n exiting with non-zero status from this script causes the 'git gc --auto'\n to abort.\n \n+post-rewrite\n+~~~~~~~~~~~~\n+\n+This hook is invoked by commands that rewrite commits (`git commit\n+--amend`, 'git-rebase', 'git-filter-branch').  Its first argument\n+denotes the command it was invoked by: currently one of `amend`,\n+`rebase`, or `filter-branch`.  Further command-dependent arguments may\n+be passed in the future.\n+\n+The hook receives a list of the rewritten commits on stdin, in the\n+format\n+\n+  <old-sha1> SP <new-sha1> [ SP <extra-info> ] LF\n+\n+The 'extra-info' is again command-dependent.  If it is empty, the\n+preceding SP is also omitted.  Currently, no commands pass any\n+'extra-info'.\n+\n+The following command-specific comments apply:\n+\n+rebase::\n+\tFor the 'squash' and 'fixup' operation, all commits that were\n+\tsquashed are listed as being rewritten to the squashed commit.\n+\tThis means that there will be several lines sharing the same\n+\t'new-sha1'.\n+\n+filter-branch::\n+\tCommits that were processed by 'git-filter-branch', but not\n+\tchanged, are not included in the list.  If the list is empty\n+\tafter this filtering, the hook is not invoked at all.\n+\n+There is no default 'post-rewrite' hook, but see the\n+`post-receive-copy-notes` script in `contrib/hooks` for an example\n+that copies your git-notes to the rewritten commits.\n+\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \n1.7.0.59.g783f8\n"},{"id":"135189","messageId":"99b0d5c4ecb129fd663e50fbb46f41ff9f824c34.1266703765.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266703765.git.trast@student.ethz.ch","subject":"[RFC PATCH v3 03/12] commit --amend: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-20T22:16:24Z","receivedAt":"2010-02-20T22:16:24Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"The rough structure of run_rewrite_hook() comes from\nrun_receive_hook() in receive-pack.\n\nWe introduce a --no-post-rewrite option and use it to avoid the hook\nwhen called from git-rebase -i 'edit'.  The next patch will add full\nsupport in git-rebase, and we only want to invoke the hook once.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n builtin-commit.c             |   43 ++++++++++++++++++++++++++++++++++\n git-rebase--interactive.sh   |    2 +-\n t/t5407-post-rewrite-hook.sh |   52 ++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 96 insertions(+), 1 deletions(-)\n create mode 100755 t/t5407-post-rewrite-hook.sh\n\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 55676fd..9b67649 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -66,6 +66,7 @@\n static char *author_name, *author_email, *author_date;\n static int all, edit_flag, also, interactive, only, amend, signoff;\n static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;\n+static int no_post_rewrite;\n static char *untracked_files_arg, *force_date;\n /*\n  * The default commit message cleanup mode will remove the lines\n@@ -137,6 +138,7 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset)\n \tOPT_BOOLEAN('z', \"null\", &null_termination,\n \t\t    \"terminate entries with NUL\"),\n \tOPT_BOOLEAN(0, \"amend\", &amend, \"amend previous commit\"),\n+\tOPT_BOOLEAN(0, \"no-post-rewrite\", &no_post_rewrite, \"bypass post-rewrite hook\"),\n \t{ OPTION_STRING, 'u', \"untracked-files\", &untracked_files_arg, \"mode\", \"show untracked files, optional modes: all, normal, no. (Default: all)\", PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n \tOPT_BOOLEAN(0, \"allow-empty\", &allow_empty, \"ok to record an empty change\"),\n \t/* end commit contents options */\n@@ -1160,6 +1162,40 @@ static int git_commit_config(const char *k, const char *v, void *cb)\n \treturn git_status_config(k, v, s);\n }\n \n+static const char post_rewrite_hook[] = \"hooks/post-rewrite\";\n+\n+static int run_rewrite_hook(const unsigned char *oldsha1,\n+\t\t\t    const unsigned char *newsha1)\n+{\n+\t/* oldsha1 SP newsha1 LF NUL */\n+\tstatic char buf[2*40 + 3];\n+\tstruct child_process proc;\n+\tconst char *argv[3];\n+\tint code;\n+\tsize_t n;\n+\n+\tif (access(git_path(post_rewrite_hook), X_OK) < 0)\n+\t\treturn 0;\n+\n+\targv[0] = git_path(post_rewrite_hook);\n+\targv[1] = \"amend\";\n+\targv[2] = NULL;\n+\n+\tmemset(&proc, 0, sizeof(proc));\n+\tproc.argv = argv;\n+\tproc.in = -1;\n+\tproc.stdout_to_stderr = 1;\n+\n+\tcode = start_command(&proc);\n+\tif (code)\n+\t\treturn code;\n+\tn = snprintf(buf, sizeof(buf), \"%s %s\\n\",\n+\t\t     sha1_to_hex(oldsha1), sha1_to_hex(newsha1));\n+\twrite_in_full(proc.in, buf, n);\n+\tclose(proc.in);\n+\treturn finish_command(&proc);\n+}\n+\n int cmd_commit(int argc, const char **argv, const char *prefix)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -1303,6 +1339,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \n \trerere(0);\n \trun_hook(get_index_file(), \"post-commit\", NULL);\n+\tif (amend && !no_post_rewrite) {\n+\t\tstruct commit *commit;\n+\t\tcommit = lookup_commit(head_sha1);\n+\t\tif (!commit || parse_commit(commit))\n+\t\t\tdie(\"HEAD commit disappeared right under my eyes?\");\n+\t\trun_rewrite_hook(commit->object.sha1, commit_sha1);\n+\t}\n \tif (!quiet)\n \t\tprint_summary(prefix, commit_sha1);\n \ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex 3e4fd14..5735859 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -445,7 +445,7 @@ do_next () {\n \t\tmark_action_done\n \t\tpick_one $sha1 ||\n \t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n-\t\tgit commit --amend\n+\t\tgit commit --amend --no-post-rewrite\n \t\t;;\n \tedit|e)\n \t\tcomment_for_reflog edit\ndiff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh\nnew file mode 100755\nindex 0000000..1020af9\n--- /dev/null\n+++ b/t/t5407-post-rewrite-hook.sh\n@@ -0,0 +1,52 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2010 Thomas Rast\n+#\n+\n+test_description='Test the post-rewrite hook.'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit A foo A &&\n+\ttest_commit B foo B &&\n+\ttest_commit C foo C &&\n+\ttest_commit D foo D\n+'\n+\n+mkdir .git/hooks\n+\n+cat >.git/hooks/post-rewrite <<EOF\n+#!/bin/sh\n+echo \\$@ > \"$TRASH_DIRECTORY\"/post-rewrite.args\n+cat > \"$TRASH_DIRECTORY\"/post-rewrite.data\n+EOF\n+chmod u+x .git/hooks/post-rewrite\n+\n+clear_hook_input () {\n+\trm -f post-rewrite.args post-rewrite.data\n+}\n+\n+verify_hook_input () {\n+\ttest_cmp \"$TRASH_DIRECTORY\"/post-rewrite.args expected.args &&\n+\ttest_cmp \"$TRASH_DIRECTORY\"/post-rewrite.data expected.data\n+}\n+\n+test_expect_success 'git commit --amend' '\n+\tclear_hook_input &&\n+\techo \"D new message\" > newmsg &&\n+\toldsha=$(git rev-parse HEAD^0) &&\n+\tgit commit -Fnewmsg --amend &&\n+\techo amend > expected.args &&\n+\techo $oldsha $(git rev-parse HEAD^0) > expected.data &&\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git commit --amend --no-post-rewrite' '\n+\tclear_hook_input &&\n+\techo \"D new message again\" > newmsg &&\n+\tgit commit --no-post-rewrite -Fnewmsg --amend &&\n+\ttest ! -f post-rewrite.args &&\n+\ttest ! -f post-rewrite.data\n+'\n+\n+test_done\n-- \n1.7.0.59.g783f8\n"},{"id":"135192","messageId":"fbeb0b749b3335953ad6e73558f0b6c2d69235d5.1266703765.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266703765.git.trast@student.ethz.ch","subject":"[RFC PATCH v3 04/12] rebase: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-20T22:16:25Z","receivedAt":"2010-02-20T22:16:25Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"We have to deal with two separate code paths: a normal rebase, which\nactually goes through git-am; and rebase {-m|-s}.\n\nThe only small issue with both is that they need to remember the\noriginal sha1 across a possible conflict resolution.  rebase -m\nalready puts this information in $dotest/current, and we just\nintroduce a similar file for git-am.\n\nNote that in git-am, the hook really only runs when coming from\ngit-rebase: the code path that sets the $dotest/original-commit file\nis guarded by a test for $dotest/rebasing.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n git-am.sh                    |   10 ++++++++++\n git-rebase.sh                |    5 +++++\n t/t5407-post-rewrite-hook.sh |   30 ++++++++++++++++++++++++++++++\n 3 files changed, 45 insertions(+), 0 deletions(-)\n\ndiff --git a/git-am.sh b/git-am.sh\nindex 3c08d53..56428b4 100755\n--- a/git-am.sh\n+++ b/git-am.sh\n@@ -575,6 +575,7 @@ do\n \t\t\techo \"Patch is empty.  Was it split wrong?\"\n \t\t\tstop_here $this\n \t\t}\n+\t\trm -f \"$dotest/original-commit\"\n \t\tif test -f \"$dotest/rebasing\" &&\n \t\t\tcommit=$(sed -e 's/^From \\([0-9a-f]*\\) .*/\\1/' \\\n \t\t\t\t-e q \"$dotest/$msgnum\") &&\n@@ -582,6 +583,7 @@ do\n \t\tthen\n \t\t\tgit cat-file commit \"$commit\" |\n \t\t\tsed -e '1,/^$/d' >\"$dotest/msg-clean\"\n+\t\t\techo \"$commit\" > \"$dotest/original-commit\"\n \t\telse\n \t\t\t{\n \t\t\t\tsed -n '/^Subject/ s/Subject: //p' \"$dotest/info\"\n@@ -768,6 +770,10 @@ do\n \tgit update-ref -m \"$GIT_REFLOG_ACTION: $FIRSTLINE\" HEAD $commit $parent ||\n \tstop_here $this\n \n+\tif test -f \"$dotest/original-commit\"; then\n+\t\techo \"$(cat \"$dotest/original-commit\") $commit\" >> \"$dotest/rewritten\"\n+\tfi\n+\n \tif test -x \"$GIT_DIR\"/hooks/post-applypatch\n \tthen\n \t\t\"$GIT_DIR\"/hooks/post-applypatch\n@@ -776,6 +782,10 @@ do\n \tgo_next\n done\n \n+if test -s \"$dotest\"/rewritten && test -x \"$GIT_DIR\"/hooks/post-rewrite; then\n+\t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$dotest\"/rewritten\n+fi\n+\n git gc --auto\n \n rm -fr \"$dotest\"\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex fb4fef7..52f8b9b 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -79,6 +79,7 @@ continue_merge () {\n \t\tthen\n \t\t\tprintf \"Committed: %0${prec}d \" $msgnum\n \t\tfi\n+\t\techo \"$cmt $(git rev-parse HEAD^0)\" >> \"$dotest/rewritten\"\n \telse\n \t\tif test -z \"$GIT_QUIET\"\n \t\tthen\n@@ -151,6 +152,10 @@ move_to_original_branch () {\n \n finish_rb_merge () {\n \tmove_to_original_branch\n+\tif test -x \"$GIT_DIR\"/hooks/post-rewrite &&\n+\t\ttest -s \"$dotest\"/rewritten; then\n+\t\t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$dotest\"/rewritten\n+\tfi\n \trm -r \"$dotest\"\n \tsay All done.\n }\ndiff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh\nindex 1020af9..1ecaa4b 100755\n--- a/t/t5407-post-rewrite-hook.sh\n+++ b/t/t5407-post-rewrite-hook.sh\n@@ -49,4 +49,34 @@ test_expect_success 'git commit --amend --no-post-rewrite' '\n \ttest ! -f post-rewrite.data\n '\n \n+test_expect_success 'git rebase' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\ttest_must_fail git rebase --onto A B &&\n+\techo C > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse C) $(git rev-parse HEAD^)\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git rebase --skip' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\ttest_must_fail git rebase --onto A B &&\n+\ttest_must_fail git rebase --skip &&\n+\techo D > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n test_done\n-- \n1.7.0.59.g783f8\n"},{"id":"135187","messageId":"edc03a936871649ad935d85c25b8a10aa9d516ab.1266703765.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266703765.git.trast@student.ethz.ch","subject":"[RFC PATCH v3 05/12] rebase -i: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-20T22:16:26Z","receivedAt":"2010-02-20T22:16:26Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Aside from the same issue that rebase also has (remembering the\noriginal commit across a conflict resolution), rebase -i brings an\nextra twist: We need to defer writing the rewritten list in the case\nof {squash,fixup} because their rewritten result should be the last\ncommit in the squashed group.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n git-rebase--interactive.sh   |   39 +++++++++++++++++++++\n t/t5407-post-rewrite-hook.sh |   78 ++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 117 insertions(+), 0 deletions(-)\n\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex 5735859..fc4f7e9 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -96,6 +96,13 @@ AUTHOR_SCRIPT=\"$DOTEST\"/author-script\n # command is processed, this file is deleted.\n AMEND=\"$DOTEST\"/amend\n \n+# For the post-rewrite hook, we make a list of rewritten commits and\n+# their new sha1s.  The rewritten-pending list keeps the sha1s of\n+# commits that have been processed, but not committed yet,\n+# e.g. because they are waiting for a 'squash' command.\n+REWRITTEN_LIST=\"$DOTEST\"/rewritten-list\n+REWRITTEN_PENDING=\"$DOTEST\"/rewritten-pending\n+\n PRESERVE_MERGES=\n STRATEGY=\n ONTO=\n@@ -198,6 +205,7 @@ make_patch () {\n }\n \n die_with_patch () {\n+\techo \"$1\" > \"$DOTEST\"/conflicted-sha\n \tmake_patch \"$1\"\n \tgit rerere\n \tdie \"$2\"\n@@ -348,6 +356,7 @@ pick_one_preserving_merges () {\n \t\t\t\tprintf \"%s\\n\" \"$msg\" > \"$GIT_DIR\"/MERGE_MSG\n \t\t\t\tdie_with_patch $sha1 \"Error redoing merge $sha1\"\n \t\t\tfi\n+\t\t\techo \"$sha1 $(git rev-parse HEAD^0)\" >> \"$REWRITTEN_LIST\"\n \t\t\t;;\n \t\t*)\n \t\t\toutput git cherry-pick \"$@\" ||\n@@ -425,6 +434,26 @@ die_failed_squash() {\n \tdie_with_patch $1 \"\"\n }\n \n+flush_rewritten_pending() {\n+\ttest -s \"$REWRITTEN_PENDING\" || return\n+\tnewsha1=\"$(git rev-parse HEAD^0)\"\n+\tsed \"s/$/ $newsha1/\" < \"$REWRITTEN_PENDING\" >> \"$REWRITTEN_LIST\"\n+\trm -f \"$REWRITTEN_PENDING\"\n+}\n+\n+record_in_rewritten() {\n+\toldsha1=\"$(git rev-parse $1)\"\n+\techo \"$oldsha1\" >> \"$REWRITTEN_PENDING\"\n+\n+\tcase \"$(peek_next_command)\" in\n+\t    squash|s|fixup|f)\n+\t\t;;\n+\t    *)\n+\t\tflush_rewritten_pending\n+\t\t;;\n+\tesac\n+}\n+\n do_next () {\n \trm -f \"$MSG\" \"$AUTHOR_SCRIPT\" \"$AMEND\" || exit\n \tread command sha1 rest < \"$TODO\"\n@@ -438,6 +467,7 @@ do_next () {\n \t\tmark_action_done\n \t\tpick_one $sha1 ||\n \t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n+\t\trecord_in_rewritten $sha1\n \t\t;;\n \treword|r)\n \t\tcomment_for_reflog reword\n@@ -446,6 +476,7 @@ do_next () {\n \t\tpick_one $sha1 ||\n \t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n \t\tgit commit --amend --no-post-rewrite\n+\t\trecord_in_rewritten $sha1\n \t\t;;\n \tedit|e)\n \t\tcomment_for_reflog edit\n@@ -454,6 +485,7 @@ do_next () {\n \t\tpick_one $sha1 ||\n \t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n \t\tmake_patch $sha1\n+\t\trecord_in_rewritten \"$sha1\"\n \t\tgit rev-parse --verify HEAD > \"$AMEND\"\n \t\twarn \"Stopped at $sha1... $rest\"\n \t\twarn \"You can amend the commit now, with\"\n@@ -509,6 +541,7 @@ do_next () {\n \t\t\trm -f \"$SQUASH_MSG\" \"$FIXUP_MSG\"\n \t\t\t;;\n \t\tesac\n+\t\trecord_in_rewritten $sha1\n \t\t;;\n \t*)\n \t\twarn \"Unknown command: $command $sha1 $rest\"\n@@ -537,6 +570,11 @@ do_next () {\n \t\ttest ! -f \"$DOTEST\"/verbose ||\n \t\t\tgit diff-tree --stat $(cat \"$DOTEST\"/head)..HEAD\n \t} &&\n+\tif test -x \"$GIT_DIR\"/hooks/post-rewrite &&\n+\t\ttest -s \"$REWRITTEN_LIST\"; then\n+\t\t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$REWRITTEN_LIST\"\n+\t\ttrue # we don't care if this hook failed\n+\tfi &&\n \trm -rf \"$DOTEST\" &&\n \tgit gc --auto &&\n \twarn \"Successfully rebased and updated $HEADNAME.\"\n@@ -685,6 +723,7 @@ first and then run 'git rebase --continue' again.\"\n \t\t\t\ttest -n \"$amend\" && git reset --soft $amend\n \t\t\t\tdie \"Could not commit staged changes.\"\n \t\t\t}\n+\t\t\trecord_in_rewritten \"$(cat \"$DOTEST\"/conflicted-sha)\"\n \t\tfi\n \n \t\trequire_clean_work_tree\ndiff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh\nindex 1ecaa4b..488d4a0 100755\n--- a/t/t5407-post-rewrite-hook.sh\n+++ b/t/t5407-post-rewrite-hook.sh\n@@ -79,4 +79,82 @@ EOF\n \tverify_hook_input\n '\n \n+test_expect_success 'git rebase -m' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\ttest_must_fail git rebase -m --onto A B &&\n+\techo C > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse C) $(git rev-parse HEAD^)\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git rebase -m --skip' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\ttest_must_fail git rebase --onto A B &&\n+\ttest_must_fail git rebase --skip &&\n+\techo D > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+. \"$TEST_DIRECTORY\"/lib-rebase.sh\n+\n+set_fake_editor\n+\n+test_expect_success 'git rebase -i (unchanged)' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\tFAKE_LINES=\"1 2\" test_must_fail git rebase -i --onto A B &&\n+\techo C > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse C) $(git rev-parse HEAD^)\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git rebase -i (skip)' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\tFAKE_LINES=\"2\" test_must_fail git rebase -i --onto A B &&\n+\techo D > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git rebase -i (squash)' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\tFAKE_LINES=\"1 squash 2\" test_must_fail git rebase -i --onto A B &&\n+\techo C > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse C) $(git rev-parse HEAD)\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n test_done\n-- \n1.7.0.59.g783f8\n"},{"id":"135200","messageId":"3559e59dd81bd3fb94196c399e1640681a323147.1266703765.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266703765.git.trast@student.ethz.ch","subject":"[RFC PATCH v3 06/12] notes: implement 'git notes copy --stdin'","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-20T22:16:27Z","receivedAt":"2010-02-20T22:16:27Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"This implements a mass-copy command that takes a sequence of lines in\nthe format\n\n  <from-sha1> SP <to-sha1> [ SP <rest> ] LF\n\non stdin, and copies each <from-sha1>'s notes to the <to-sha1>.  The\n<rest> is ignored.  The intent, of course, is that this can read the\nsame input that the 'post-rewrite' hook gets.\n\nThe copy_note() function is exposed for everyone's and in particular\nthe next commit's use.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n Documentation/git-notes.txt |   12 ++++++++-\n builtin-notes.c             |   57 ++++++++++++++++++++++++++++++++++++++++++-\n notes.c                     |   21 ++++++++++++++++\n notes.h                     |    9 +++++++\n t/t3301-notes.sh            |   32 ++++++++++++++++++++++++\n 5 files changed, 129 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex 14f73b9..f67cb6a 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n [verse]\n 'git notes' [list [<object>]]\n 'git notes' add [-f] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]\n-'git notes' copy [-f] <from-object> <to-object>\n+'git notes' copy [-f] ( --stdin | <from-object> <to-object> )\n 'git notes' append [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]\n 'git notes' edit [<object>]\n 'git notes' show [<object>]\n@@ -55,6 +55,16 @@ copy::\n \tobjects has none. (use -f to overwrite existing notes to the\n \tsecond object). This subcommand is equivalent to:\n \t`git notes add [-f] -C $(git notes list <from-object>) <to-object>`\n++\n+In `\\--stdin` mode, take lines in the format\n++\n+----------\n+<from-object> SP <to-object> [ SP <rest> ] LF\n+----------\n++\n+on standard input, and copy the notes from each <from-object> to its\n+corresponding <to-object>.  (The optional `<rest>` is ignored so that\n+the command can read the input given to the `post-rewrite` hook.)\n \n append::\n \tAppend to the notes of an existing object (defaults to HEAD).\ndiff --git a/builtin-notes.c b/builtin-notes.c\nindex 123ecad..62f6ceb 100644\n--- a/builtin-notes.c\n+++ b/builtin-notes.c\n@@ -278,6 +278,47 @@ int commit_notes(struct notes_tree *t, const char *msg)\n \treturn 0;\n }\n \n+int notes_copy_from_stdin(int force)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct notes_tree *t;\n+\tstruct notes_rewrite_cfg *c = NULL;\n+\tint ret = 0;\n+\n+\tinit_notes(NULL, NULL, NULL, 0);\n+\tt = &default_notes_tree;\n+\n+\twhile (strbuf_getline(&buf, stdin, '\\n') != EOF) {\n+\t\tunsigned char from_obj[20], to_obj[20];\n+\t\tstruct strbuf **split;\n+\t\tint err;\n+\n+\t\tsplit = strbuf_split(&buf, ' ');\n+\t\tif (!split[0] || !split[1])\n+\t\t\tdie(\"Malformed input line: '%s'.\", buf.buf);\n+\t\tstrbuf_rtrim(split[0]);\n+\t\tstrbuf_rtrim(split[1]);\n+\t\tif (get_sha1(split[0]->buf, from_obj))\n+\t\t\tdie(\"Failed to resolve '%s' as a valid ref.\", split[0]->buf);\n+\t\tif (get_sha1(split[1]->buf, to_obj))\n+\t\t\tdie(\"Failed to resolve '%s' as a valid ref.\", split[1]->buf);\n+\n+\t\terr = copy_note(t, from_obj, to_obj, force, combine_notes_overwrite);\n+\n+\t\tif (err) {\n+\t\t\terror(\"Failed to copy notes from '%s' to '%s'\",\n+\t\t\t      split[0]->buf, split[1]->buf);\n+\t\t\tret = 1;\n+\t\t}\n+\n+\t\tstrbuf_list_free(split);\n+\t}\n+\n+\tcommit_notes(t, \"Notes added by 'git notes copy'\");\n+\tfree_notes(t);\n+\treturn ret;\n+}\n+\n int cmd_notes(int argc, const char **argv, const char *prefix)\n {\n \tstruct notes_tree *t;\n@@ -287,7 +328,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \tchar logmsg[100];\n \n \tint list = 0, add = 0, copy = 0, append = 0, edit = 0, show = 0,\n-\t    remove = 0, prune = 0, force = 0;\n+\t    remove = 0, prune = 0, force = 0, from_stdin = 0;\n \tint given_object = 0, i = 1, retval = 0;\n \tstruct msg_arg msg = { 0, 0, STRBUF_INIT };\n \tstruct option options[] = {\n@@ -301,6 +342,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\tOPT_CALLBACK('C', \"reuse-message\", &msg, \"OBJECT\",\n \t\t\t   \"reuse specified note object\", parse_reuse_arg),\n \t\tOPT_BOOLEAN('f', \"force\", &force, \"replace existing notes\"),\n+\t\tOPT_BOOLEAN(0, \"stdin\", &from_stdin, \"read objects from stdin\"),\n \t\tOPT_END()\n \t};\n \n@@ -349,8 +391,21 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(git_notes_usage, options);\n \t}\n \n+\tif (!copy && from_stdin) {\n+\t\terror(\"cannot use --stdin with %s subcommand.\", argv[0]);\n+\t\tusage_with_options(git_notes_usage, options);\n+\t}\n+\n \tif (copy) {\n \t\tconst char *from_ref;\n+\t\tif (from_stdin) {\n+\t\t\tif (argc > 1) {\n+\t\t\t\terror(\"too many parameters\");\n+\t\t\t\tusage_with_options(git_notes_usage, options);\n+\t\t\t} else {\n+\t\t\t\treturn notes_copy_from_stdin(force);\n+\t\t\t}\n+\t\t}\n \t\tif (argc < 3) {\n \t\t\terror(\"too few parameters\");\n \t\t\tusage_with_options(git_notes_usage, options);\ndiff --git a/notes.c b/notes.c\nindex d37178b..b194870 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -1122,3 +1122,24 @@ void format_display_notes(const unsigned char *object_sha1,\n \t\tformat_note(display_notes_trees[i], object_sha1, sb,\n \t\t\t    output_encoding, flags);\n }\n+\n+int copy_note(struct notes_tree *t,\n+\t      const unsigned char *from_obj, const unsigned char *to_obj,\n+\t      int force, combine_notes_fn combine_fn)\n+{\n+\tconst unsigned char *note;\n+\n+\tnote = get_note(t, from_obj);\n+\tif (!force) {\n+\t\tconst unsigned char *existing_note = get_note(t, to_obj);\n+\t\tif (existing_note)\n+\t\t\treturn 1;\n+\t}\n+\n+\tif (note)\n+\t\tadd_note(t, to_obj, note, combine_fn);\n+\telse\n+\t\tremove_note(t, to_obj);\n+\n+\treturn 0;\n+}\ndiff --git a/notes.h b/notes.h\nindex 32ae2f0..fb09537 100644\n--- a/notes.h\n+++ b/notes.h\n@@ -100,6 +100,15 @@ void add_note(struct notes_tree *t, const unsigned char *object_sha1,\n \t\tconst unsigned char *object_sha1);\n \n /*\n+ * Copy a note from one object to another in the given notes_tree.\n+ *\n+ * Fails if the to_obj already has a note unless 'force' is true.\n+ */\n+int copy_note(struct notes_tree *t,\n+\t      const unsigned char *from_obj, const unsigned char *to_obj,\n+\t      int force, combine_notes_fn combine_fn);\n+\n+/*\n  * Flags controlling behaviour of for_each_note()\n  *\n  * Default behaviour of for_each_note() is to traverse every single note object\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex 0dfff82..634d213 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -683,4 +683,36 @@ test_expect_success 'cannot copy note from object without notes' '\n \ttest_must_fail git notes copy HEAD^ HEAD\n '\n \n+cat > expect << EOF\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:25:13 2005 -0700\n+\n+    13th\n+\n+Notes from notes/other:\n+    yet another note\n+$whitespace\n+    yet another note\n+\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:24:13 2005 -0700\n+\n+    12th\n+\n+Notes from notes/other:\n+    other note\n+$whitespace\n+    yet another note\n+EOF\n+\n+test_expect_success 'git notes copy --stdin' '\n+\t(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \\\n+\techo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |\n+\tgit notes copy --stdin &&\n+\tgit log -2 > output &&\n+\tstrip_then_cmp expect output &&\n+\ttest \"$(git notes list HEAD)\" = \"$(git notes list HEAD~2)\" &&\n+\ttest \"$(git notes list HEAD^)\" = \"$(git notes list HEAD~3)\"\n+'\n+\n test_done\n-- \n1.7.0.59.g783f8\n"},{"id":"135191","messageId":"9f4cb8a577e853378c1d3136b54a5e73ac29dc22.1266703765.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266703765.git.trast@student.ethz.ch","subject":"[RFC PATCH v3 07/12] notes: implement helpers needed for note copying during rewrite","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-20T22:16:28Z","receivedAt":"2010-02-20T22:16:28Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Implement helper functions to load the rewriting config, and to\nactually copy the notes.  Also document the config.\n\nSecondly, also implement an undocumented --for-rewrite=<cmd> option to\n'git notes copy' which is used like --stdin, but also puts the\nconfiguration for <cmd> into effect.  It will be needed to support the\ncopying in git-rebase.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n Documentation/config.txt    |   23 ++++++++\n Documentation/git-notes.txt |    4 ++\n Documentation/githooks.txt  |    4 ++\n builtin-notes.c             |  121 ++++++++++++++++++++++++++++++++++++++++---\n builtin.h                   |   16 ++++++\n t/t3301-notes.sh            |  118 +++++++++++++++++++++++++++++++++++++++++-\n 6 files changed, 277 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 3c5cfec..9c3b337 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1311,6 +1311,29 @@ The effective value of \"core.notesRef\" (possibly overridden by\n GIT_NOTES_REF) is also implicitly added to the list of refs to be\n displayed.\n \n+notes.rewrite.<command>::\n+\tWhen rewriting commits with <command> (currently `amend` or\n+\t`rebase`) and this variable is set to `true`, git\n+\tautomatically copies your notes from the original to the\n+\trewritten commit.  Defaults to `true`, but see\n+\t\"notes.rewriteRef\" below.\n+\n+notes.rewriteMode::\n+\tWhen copying notes during a rewrite (see the\n+\t\"notes.rewrite.<command>\" option), determines what to do if\n+\tthe target commit already has a note.  Must be one of\n+\t`overwrite`, `concatenate`, or `ignore`.  Defaults to\n+\t`concatenate`.\n+\n+notes.rewriteRef::\n+\tWhen copying notes during a rewrite, specifies the ref whose\n+\tnotes should be copied.  The ref may be a glob, in which case\n+\tnotes in all matching refs will be copied.  You may also\n+\tspecify this configuration several times.\n++\n+Does not have a default value; you must configure this variable to\n+enable note rewriting.\n+\n pack.window::\n \tThe size of the window used by linkgit:git-pack-objects[1] when no\n \twindow size is given on the command line. Defaults to 10.\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex f67cb6a..92f1249 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -34,6 +34,10 @@ the empty string.  Alternatively, you can set it to a different ref,\n something like \"refs/notes/bugzilla\".  This setting can be overridden\n by the environment variable \"GIT_NOTES_REF\".\n \n+See the description of \"notes.rewrite.<command>\" in\n+linkgit:git-config[1] for a way of carrying your notes across commands\n+that rewrite commits.\n+\n \n SUBCOMMANDS\n -----------\ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex 41895e9..c33a38e 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -335,6 +335,10 @@ The 'extra-info' is again command-dependent.  If it is empty, the\n preceding SP is also omitted.  Currently, no commands pass any\n 'extra-info'.\n \n+The hook always runs after the automatic note copying (see\n+\"notes.rewrite.<command>\" in linkgit:git-config.txt) has happened, and\n+thus has access to these notes.\n+\n The following command-specific comments apply:\n \n rebase::\ndiff --git a/builtin-notes.c b/builtin-notes.c\nindex 62f6ceb..19dd3ab 100644\n--- a/builtin-notes.c\n+++ b/builtin-notes.c\n@@ -16,6 +16,7 @@\n #include \"exec_cmd.h\"\n #include \"run-command.h\"\n #include \"parse-options.h\"\n+#include \"string-list.h\"\n \n static const char * const git_notes_usage[] = {\n \t\"git notes [list [<object>]]\",\n@@ -278,15 +279,104 @@ int commit_notes(struct notes_tree *t, const char *msg)\n \treturn 0;\n }\n \n-int notes_copy_from_stdin(int force)\n+\n+combine_notes_fn *parse_combine_notes_fn(const char *v)\n+{\n+\tif (!strcasecmp(v, \"overwrite\"))\n+\t\treturn combine_notes_overwrite;\n+\telse if (!strcasecmp(v, \"ignore\"))\n+\t\treturn combine_notes_ignore;\n+\telse if (!strcasecmp(v, \"concatenate\"))\n+\t\treturn combine_notes_concatenate;\n+\telse\n+\t\treturn NULL;\n+}\n+\n+static int notes_rewrite_config(const char *k, const char *v, void *cb)\n+{\n+\tstruct notes_rewrite_cfg *c = cb;\n+\tif (!prefixcmp(k, \"notes.rewrite.\") && !strcmp(k+14, c->cmd)) {\n+\t\tc->enabled = git_config_bool(k, v);\n+\t\treturn 0;\n+\t} else if (!strcmp(k, \"notes.rewritemode\")) {\n+\t\tc->combine = parse_combine_notes_fn(v);\n+\t\tif (!c->combine) {\n+\t\t\terror(\"Bad notes.rewriteMode value: '%s'\", v);\n+\t\t\treturn 1;\n+\t\t}\n+\t\treturn 0;\n+\t} else if (!strcmp(k, \"notes.rewriteref\")) {\n+\t\t/* note that a refs/ prefix is implied in the\n+\t\t * underlying for_each_glob_ref */\n+\t\tif (!prefixcmp(v, \"refs/notes/\") || !prefixcmp(v, \"notes/\"))\n+\t\t\tstring_list_add_refs_by_glob(c->refs, v);\n+\t\telse\n+\t\t\twarning(\"Refusing to rewrite notes in %s\"\n+\t\t\t\t\" (outside of refs/notes/)\", v);\n+\t\treturn 0;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+\n+struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)\n+{\n+\tstruct notes_rewrite_cfg *c = xmalloc(sizeof(struct notes_rewrite_cfg));\n+\tc->cmd = cmd;\n+\tc->enabled = 1;\n+\tc->combine = combine_notes_concatenate;\n+\tc->refs = xcalloc(1, sizeof(struct string_list));\n+\tc->refs->strdup_strings = 1;\n+\tgit_config(notes_rewrite_config, c);\n+\tif (!c->enabled || !c->refs->nr) {\n+\t\tstring_list_clear(c->refs, 0);\n+\t\tfree(c->refs);\n+\t\tfree(c);\n+\t\treturn NULL;\n+\t}\n+\tc->trees = load_notes_trees(c->refs);\n+\tstring_list_clear(c->refs, 0);\n+\tfree(c->refs);\n+\treturn c;\n+}\n+\n+int copy_note_for_rewrite(struct notes_rewrite_cfg *c,\n+\t\t\t  const unsigned char *from_obj, const unsigned char *to_obj)\n+{\n+\tint ret = 0;\n+\tint i;\n+\tfor (i = 0; c->trees[i]; i++)\n+\t\tret = copy_note(c->trees[i], from_obj, to_obj, 1, c->combine) || ret;\n+\treturn ret;\n+}\n+\n+void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c)\n+{\n+\tint i;\n+\tfor (i = 0; c->trees[i]; i++) {\n+\t\tcommit_notes(c->trees[i], \"Notes added by 'git notes copy'\");\n+\t\tfree_notes(c->trees[i]);\n+\t}\n+\tfree(c->trees);\n+\tfree(c);\n+}\n+\n+int notes_copy_from_stdin(int force, const char *rewrite_cmd)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct notes_tree *t;\n \tstruct notes_rewrite_cfg *c = NULL;\n \tint ret = 0;\n \n-\tinit_notes(NULL, NULL, NULL, 0);\n-\tt = &default_notes_tree;\n+\tif (rewrite_cmd) {\n+\t\tc = init_copy_notes_for_rewrite(rewrite_cmd);\n+\t\tif (!c)\n+\t\t\treturn 0;\n+\t} else {\n+\t\tinit_notes(NULL, NULL, NULL, 0);\n+\t\tt = &default_notes_tree;\n+\t}\n \n \twhile (strbuf_getline(&buf, stdin, '\\n') != EOF) {\n \t\tunsigned char from_obj[20], to_obj[20];\n@@ -303,7 +393,11 @@ int notes_copy_from_stdin(int force)\n \t\tif (get_sha1(split[1]->buf, to_obj))\n \t\t\tdie(\"Failed to resolve '%s' as a valid ref.\", split[1]->buf);\n \n-\t\terr = copy_note(t, from_obj, to_obj, force, combine_notes_overwrite);\n+\t\tif (rewrite_cmd)\n+\t\t\terr = copy_note_for_rewrite(c, from_obj, to_obj);\n+\t\telse\n+\t\t\terr = copy_note(t, from_obj, to_obj, force,\n+\t\t\t\t\tcombine_notes_overwrite);\n \n \t\tif (err) {\n \t\t\terror(\"Failed to copy notes from '%s' to '%s'\",\n@@ -314,8 +408,12 @@ int notes_copy_from_stdin(int force)\n \t\tstrbuf_list_free(split);\n \t}\n \n-\tcommit_notes(t, \"Notes added by 'git notes copy'\");\n-\tfree_notes(t);\n+\tif (!rewrite_cmd) {\n+\t\tcommit_notes(t, \"Notes added by 'git notes copy'\");\n+\t\tfree_notes(t);\n+\t} else {\n+\t\tfinish_copy_notes_for_rewrite(c);\n+\t}\n \treturn ret;\n }\n \n@@ -331,6 +429,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t    remove = 0, prune = 0, force = 0, from_stdin = 0;\n \tint given_object = 0, i = 1, retval = 0;\n \tstruct msg_arg msg = { 0, 0, STRBUF_INIT };\n+\tconst char *rewrite_cmd = NULL;\n \tstruct option options[] = {\n \t\tOPT_GROUP(\"Notes options\"),\n \t\tOPT_CALLBACK('m', \"message\", &msg, \"MSG\",\n@@ -343,6 +442,8 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\t\t   \"reuse specified note object\", parse_reuse_arg),\n \t\tOPT_BOOLEAN('f', \"force\", &force, \"replace existing notes\"),\n \t\tOPT_BOOLEAN(0, \"stdin\", &from_stdin, \"read objects from stdin\"),\n+\t\tOPT_STRING(0, \"for-rewrite\", &rewrite_cmd, \"command\",\n+\t\t\t   \"load rewriting config for <command> (implies --stdin)\"),\n \t\tOPT_END()\n \t};\n \n@@ -391,6 +492,10 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(git_notes_usage, options);\n \t}\n \n+\tif (!copy && rewrite_cmd) {\n+\t\terror(\"cannot use --for-rewrite with %s subcommand.\", argv[0]);\n+\t\tusage_with_options(git_notes_usage, options);\n+\t}\n \tif (!copy && from_stdin) {\n \t\terror(\"cannot use --stdin with %s subcommand.\", argv[0]);\n \t\tusage_with_options(git_notes_usage, options);\n@@ -398,12 +503,12 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \n \tif (copy) {\n \t\tconst char *from_ref;\n-\t\tif (from_stdin) {\n+\t\tif (from_stdin || rewrite_cmd) {\n \t\t\tif (argc > 1) {\n \t\t\t\terror(\"too many parameters\");\n \t\t\t\tusage_with_options(git_notes_usage, options);\n \t\t\t} else {\n-\t\t\t\treturn notes_copy_from_stdin(force);\n+\t\t\t\treturn notes_copy_from_stdin(force, rewrite_cmd);\n \t\t\t}\n \t\t}\n \t\tif (argc < 3) {\ndiff --git a/builtin.h b/builtin.h\nindex cdf9847..1508db6 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -20,6 +20,22 @@ extern int commit_tree(const char *msg, unsigned char *tree,\n \t\tstruct commit_list *parents, unsigned char *ret,\n \t\tconst char *author);\n extern int commit_notes(struct notes_tree *t, const char *msg);\n+\n+struct notes_rewrite_cfg\n+{\n+\tstruct notes_tree **trees;\n+\tconst char *cmd;\n+\tint enabled;\n+\tcombine_notes_fn *combine;\n+\tstruct string_list *refs;\n+};\n+\n+combine_notes_fn *parse_combine_notes_fn(const char *v);\n+struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd);\n+int copy_note_for_rewrite(struct notes_rewrite_cfg *c,\n+\t\t\t  const unsigned char *from_obj, const unsigned char *to_obj);\n+void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);\n+\n extern int check_pager_config(const char *cmd);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex 634d213..5a0dccd 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -684,6 +684,7 @@ test_expect_success 'cannot copy note from object without notes' '\n '\n \n cat > expect << EOF\n+commit e5d4fb5698d564ab8c73551538ecaf2b0c666185\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:25:13 2005 -0700\n \n@@ -694,6 +695,7 @@ Notes from notes/other:\n $whitespace\n     yet another note\n \n+commit 7038787dfe22a14c3867ce816dbba39845359719\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:24:13 2005 -0700\n \n@@ -710,9 +712,123 @@ test_expect_success 'git notes copy --stdin' '\n \techo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |\n \tgit notes copy --stdin &&\n \tgit log -2 > output &&\n-\tstrip_then_cmp expect output &&\n+\ttest_cmp expect output &&\n \ttest \"$(git notes list HEAD)\" = \"$(git notes list HEAD~2)\" &&\n \ttest \"$(git notes list HEAD^)\" = \"$(git notes list HEAD~3)\"\n '\n \n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:26:13 2005 -0700\n+\n+    14th\n+EOF\n+\n+test_expect_success 'git notes copy --for-rewrite (unconfigured)' '\n+\ttest_commit 14th &&\n+\ttest_commit 15th &&\n+\t(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \\\n+\techo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes from notes/other:\n+    yet another note\n+$whitespace\n+    yet another note\n+\n+commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:26:13 2005 -0700\n+\n+    14th\n+\n+Notes from notes/other:\n+    other note\n+$whitespace\n+    yet another note\n+EOF\n+\n+test_expect_success 'git notes copy --for-rewrite (enabled)' '\n+\tgit config notes.rewriteMode overwrite &&\n+\tgit config notes.rewriteRef \"refs/notes/*\" &&\n+\t(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \\\n+\techo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'git notes copy --for-rewrite (disabled)' '\n+\tgit config notes.rewrite.bar false &&\n+\techo $(git rev-parse HEAD~3) $(git rev-parse HEAD) |\n+\tgit notes copy --for-rewrite=bar &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes from notes/other:\n+    a fresh note\n+EOF\n+\n+test_expect_success 'git notes copy --for-rewrite (overwrite)' '\n+\tgit notes add -f -m\"a fresh note\" HEAD^ &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'git notes copy --for-rewrite (ignore)' '\n+\tgit config notes.rewriteMode ignore &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes from notes/other:\n+    a fresh note\n+    another fresh note\n+EOF\n+\n+test_expect_success 'git notes copy --for-rewrite (append)' '\n+\tgit notes add -f -m\"another fresh note\" HEAD^ &&\n+\tgit config notes.rewriteMode concatenate &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n test_done\n-- \n1.7.0.59.g783f8\n"},{"id":"135196","messageId":"f53faede5db3cf448ec67448e21da8983003514c.1266703765.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266703765.git.trast@student.ethz.ch","subject":"[RFC PATCH v3 08/12] rebase: support automatic notes copying","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-20T22:16:29Z","receivedAt":"2010-02-20T22:16:29Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Luckily, all the support already happens to be there.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n git-am.sh                     |    5 ++++-\n git-rebase--interactive.sh    |    4 ++++\n git-rebase.sh                 |    1 +\n t/t3400-rebase.sh             |   17 +++++++++++++++++\n t/t3404-rebase-interactive.sh |   11 +++++++++++\n 5 files changed, 37 insertions(+), 1 deletions(-)\n\ndiff --git a/git-am.sh b/git-am.sh\nindex 56428b4..83cf6e7 100755\n--- a/git-am.sh\n+++ b/git-am.sh\n@@ -782,8 +782,11 @@ do\n \tgo_next\n done\n \n-if test -s \"$dotest\"/rewritten && test -x \"$GIT_DIR\"/hooks/post-rewrite; then\n+if test -s \"$dotest\"/rewritten; then\n+    git notes copy --for-rewrite=rebase < \"$dotest\"/rewritten\n+    if test -x \"$GIT_DIR\"/hooks/post-rewrite; then\n \t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$dotest\"/rewritten\n+    fi\n fi\n \n git gc --auto\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex fc4f7e9..1fda620 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -570,6 +570,10 @@ do_next () {\n \t\ttest ! -f \"$DOTEST\"/verbose ||\n \t\t\tgit diff-tree --stat $(cat \"$DOTEST\"/head)..HEAD\n \t} &&\n+\t{\n+\t\tgit notes copy --for-rewrite=rebase < \"$REWRITTEN_LIST\" ||\n+\t\ttrue # we don't care if this copying failed\n+\t} &&\n \tif test -x \"$GIT_DIR\"/hooks/post-rewrite &&\n \t\ttest -s \"$REWRITTEN_LIST\"; then\n \t\t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$REWRITTEN_LIST\"\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex 52f8b9b..e0eb956 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -152,6 +152,7 @@ move_to_original_branch () {\n \n finish_rb_merge () {\n \tmove_to_original_branch\n+\tgit notes copy --for-rewrite=rebase < \"$dotest\"/rewritten\n \tif test -x \"$GIT_DIR\"/hooks/post-rewrite &&\n \t\ttest -s \"$dotest\"/rewritten; then\n \t\t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$dotest\"/rewritten\ndiff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh\nindex 4314ad2..dbf7dfb 100755\n--- a/t/t3400-rebase.sh\n+++ b/t/t3400-rebase.sh\n@@ -151,4 +151,21 @@ test_expect_success 'Rebase a commit that sprinkles CRs in' '\n \tgit diff --exit-code file-with-cr:CR HEAD:CR\n '\n \n+test_expect_success 'rebase can copy notes' '\n+\tgit config notes.rewrite.rebase true &&\n+\tgit config notes.rewriteRef \"refs/notes/*\" &&\n+\ttest_commit n1 &&\n+\ttest_commit n2 &&\n+\ttest_commit n3 &&\n+\tgit notes add -m\"a note\" n3 &&\n+\tgit rebase --onto n1 n2 &&\n+\ttest \"a note\" = \"$(git notes show HEAD)\"\n+'\n+\n+test_expect_success 'rebase -m can copy notes' '\n+\tgit reset --hard n3 &&\n+\tgit rebase -m --onto n1 n2 &&\n+\ttest \"a note\" = \"$(git notes show HEAD)\"\n+'\n+\n test_done\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 4e35137..a5c5008 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -553,4 +553,15 @@ test_expect_success 'reword' '\n \tgit show HEAD~2 | grep \"C changed\"\n '\n \n+test_expect_success 'rebase -i can copy notes' '\n+\tgit config notes.rewrite.rebase true &&\n+\tgit config notes.rewriteRef \"refs/notes/*\" &&\n+\ttest_commit n1 &&\n+\ttest_commit n2 &&\n+\ttest_commit n3 &&\n+\tgit notes add -m\"a note\" n3 &&\n+\tgit rebase --onto n1 n2 &&\n+\ttest \"a note\" = \"$(git notes show HEAD)\"\n+'\n+\n test_done\n-- \n1.7.0.59.g783f8\n"},{"id":"135190","messageId":"a51b55e5d872bbd9c6a89cac0c453505a27fb7b1.1266703765.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266703765.git.trast@student.ethz.ch","subject":"[RFC PATCH v3 09/12] commit --amend: copy notes to the new commit","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-20T22:16:30Z","receivedAt":"2010-02-20T22:16:30Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Teaches 'git commit --amend' to copy notes.  The catch is that this\nmust also be guarded by --no-post-rewrite, which we use to prevent\n--amend from copying notes during a rebase -i 'edit'/'reword'.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n builtin-commit.c  |    6 ++++++\n t/t7501-commit.sh |   12 ++++++++++++\n 2 files changed, 18 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 9b67649..1943615 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -1341,9 +1341,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \trun_hook(get_index_file(), \"post-commit\", NULL);\n \tif (amend && !no_post_rewrite) {\n \t\tstruct commit *commit;\n+\t\tstruct notes_rewrite_cfg *cfg;\n \t\tcommit = lookup_commit(head_sha1);\n \t\tif (!commit || parse_commit(commit))\n \t\t\tdie(\"HEAD commit disappeared right under my eyes?\");\n+\t\tcfg = init_copy_notes_for_rewrite(\"amend\");\n+\t\tif (cfg) {\n+\t\t\tcopy_note_for_rewrite(cfg, commit->object.sha1, commit_sha1);\n+\t\t\tfinish_copy_notes_for_rewrite(cfg);\n+\t\t}\n \t\trun_rewrite_hook(commit->object.sha1, commit_sha1);\n \t}\n \tif (!quiet)\ndiff --git a/t/t7501-commit.sh b/t/t7501-commit.sh\nindex 7940901..8297cb4 100755\n--- a/t/t7501-commit.sh\n+++ b/t/t7501-commit.sh\n@@ -425,4 +425,16 @@ test_expect_success 'amend using the message from a commit named with tag' '\n \n '\n \n+test_expect_success 'amend can copy notes' '\n+\n+\tgit config notes.rewrite.amend true &&\n+\tgit config notes.rewriteRef \"refs/notes/*\" &&\n+\ttest_commit foo &&\n+\tgit notes add -m\"a note\" &&\n+\ttest_tick &&\n+\tgit commit --amend -m\"new foo\" &&\n+\ttest \"$(git notes show)\" = \"a note\"\n+\n+'\n+\n test_done\n-- \n1.7.0.59.g783f8\n"},{"id":"135188","messageId":"dc9d990e10afbe262bbb5e12a8c33483bdd15198.1266703765.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266703765.git.trast@student.ethz.ch","subject":"[RFC PATCH v3 10/12] filter-branch: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-20T22:16:31Z","receivedAt":"2010-02-20T22:16:31Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"With a twist: since this is supposed to be the be-all end-all of\nrewriting, we give the user the chance to override the hook.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n Documentation/git-filter-branch.txt |    7 ++++++-\n git-filter-branch.sh                |   21 +++++++++++++++++++++\n t/t5407-post-rewrite-hook.sh        |   28 ++++++++++++++++++++++++++++\n 3 files changed, 55 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt\nindex 020028c..28a705f 100644\n--- a/Documentation/git-filter-branch.txt\n+++ b/Documentation/git-filter-branch.txt\n@@ -12,7 +12,7 @@ SYNOPSIS\n \t[--index-filter <command>] [--parent-filter <command>]\n \t[--msg-filter <command>] [--commit-filter <command>]\n \t[--tag-name-filter <command>] [--subdirectory-filter <directory>]\n-\t[--prune-empty]\n+\t[--prune-empty] [--post-rewrite <command>]\n \t[--original <namespace>] [-d <directory>] [-f | --force]\n \t[--] [<rev-list options>...]\n \n@@ -182,6 +182,11 @@ the nearest ancestor that was not excluded.\n \tof the `git commit-tree \"$@\"` idiom in your commit filter to make that\n \thappen.\n \n+--post-rewrite <command>::\n+\tOverrides the post-rewrite hook (if you have one).  To bypass\n+\tthe hook for this invocation, pass `:`.  See\n+\tlinkgit:githooks[1].\n+\n --original <namespace>::\n \tUse this option to set the namespace where the original commits\n \twill be stored. The default value is 'refs/original'.\ndiff --git a/git-filter-branch.sh b/git-filter-branch.sh\nindex 88fb0f0..301c497 100755\n--- a/git-filter-branch.sh\n+++ b/git-filter-branch.sh\n@@ -101,6 +101,7 @@ USAGE=\"[--env-filter <command>] [--tree-filter <command>]\n             [--index-filter <command>] [--parent-filter <command>]\n             [--msg-filter <command>] [--commit-filter <command>]\n             [--tag-name-filter <command>] [--subdirectory-filter <directory>]\n+\t    [--post-rewrite <command>]\n             [--original <namespace>] [-d <directory>] [-f | --force]\n             [<rev-list options>...]\"\n \n@@ -126,6 +127,13 @@ orig_namespace=refs/original/\n force=\n prune_empty=\n remap_to_ancestor=\n+\n+if test -x \"$GIT_DIR\"/hooks/post-rewrite; then\n+\tpost_rewrite=$(git rev-parse --sq-quote \"$GIT_DIR\"/hooks/post-rewrite)\n+else\n+\tpost_rewrite=:\n+fi\n+\n while :\n do\n \tcase \"$1\" in\n@@ -190,6 +198,10 @@ do\n \t\tfilter_subdir=\"$OPTARG\"\n \t\tremap_to_ancestor=t\n \t\t;;\n+\t--post-rewrite)\n+\t\tpost_rewrite=\"$OPTARG\"\n+\t\tpost_rewrite_given=t\n+\t\t;;\n \t--original)\n \t\torig_namespace=$(expr \"$OPTARG/\" : '\\(.*[^/]\\)/*$')/\n \t\t;;\n@@ -358,6 +370,10 @@ while read commit parents; do\n \t@SHELL_PATH@ -c \"$filter_commit\" \"git commit-tree\" \\\n \t\t$(git write-tree) $parentstr < ../message > ../map/$commit ||\n \t\t\tdie \"could not write rewritten commit\"\n+\tnew_commit=$(map $commit)\n+\tif test $commit != $new_commit; then\n+\t\techo $commit $new_commit >> \"$workdir\"/../rewritten\n+\tfi\n done <../revs\n \n # If we are filtering for paths, as in the case of a subdirectory\n@@ -484,6 +500,11 @@ if [ \"$filter_tag_name\" ]; then\n fi\n \n cd ../..\n+\n+if test -s \"$workdir\"/../rewritten; then\n+\teval \"$post_rewrite\" 'filter-branch < \"$workdir\"/../rewritten'\n+fi\n+\n rm -rf \"$tempdir\"\n \n trap - 0\ndiff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh\nindex 488d4a0..fa81fd4 100755\n--- a/t/t5407-post-rewrite-hook.sh\n+++ b/t/t5407-post-rewrite-hook.sh\n@@ -157,4 +157,32 @@ EOF\n \tverify_hook_input\n '\n \n+test_expect_success 'git filter-branch' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\tgit filter-branch -f --tree-filter \"touch newfile\" B..HEAD &&\n+\techo filter-branch >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse C) $(git rev-parse HEAD^)\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git filter-branch (no-op)' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\tgit filter-branch -f B..HEAD &&\n+\ttest ! -f post-rewrite.args &&\n+\ttest ! -f post-rewrite.data\n+'\n+\n+test_expect_success 'git filter-branch --post-rewrite :' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\tgit filter-branch -f --post-rewrite : --tree-filter \"touch newfile\" B..HEAD &&\n+\ttest ! -f post-rewrite.args &&\n+\ttest ! -f post-rewrite.data\n+'\n+\n test_done\n-- \n1.7.0.59.g783f8\n"},{"id":"135197","messageId":"09f864c096dd15378ebabbc294c959da555da5fa.1266703765.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266703765.git.trast@student.ethz.ch","subject":"[RFC PATCH v3 11/12] filter-branch: learn how to filter notes","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-20T22:16:32Z","receivedAt":"2010-02-20T22:16:32Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Teach filter-branch a new option --notes-filter that acts as a pipe\nfilter (much like --msg-filter) for the commit notes.  For consistency\nwith the note copying support in amend/rebase, it defaults to 'cat' if\nnotes.rewrite.filter-branch is set.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n Documentation/config.txt            |    8 ++++++--\n Documentation/git-filter-branch.txt |   13 +++++++++++++\n Documentation/githooks.txt          |    8 ++++----\n git-filter-branch.sh                |   18 +++++++++++++++++-\n t/t7003-filter-branch.sh            |   22 ++++++++++++++++++++++\n 5 files changed, 62 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 9c3b337..5e1d734 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1312,8 +1312,8 @@ GIT_NOTES_REF) is also implicitly added to the list of refs to be\n displayed.\n \n notes.rewrite.<command>::\n-\tWhen rewriting commits with <command> (currently `amend` or\n-\t`rebase`) and this variable is set to `true`, git\n+\tWhen rewriting commits with <command> (currently `amend`,\n+\t`rebase` or `filter-branch`) and this variable is set to `true`, git\n \tautomatically copies your notes from the original to the\n \trewritten commit.  Defaults to `true`, but see\n \t\"notes.rewriteRef\" below.\n@@ -1324,6 +1324,10 @@ notes.rewriteMode::\n \tthe target commit already has a note.  Must be one of\n \t`overwrite`, `concatenate`, or `ignore`.  Defaults to\n \t`concatenate`.\n++\n+Note that 'git-filter-branch' ignores this setting and always\n+overwrites; see the description of `--notes-filter` in\n+linkgit:git-filter-branch[1].\n \n notes.rewriteRef::\n \tWhen copying notes during a rewrite, specifies the ref whose\ndiff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt\nindex 28a705f..6fd97d7 100644\n--- a/Documentation/git-filter-branch.txt\n+++ b/Documentation/git-filter-branch.txt\n@@ -161,6 +161,19 @@ to other tags will be rewritten to point to the underlying commit.\n \tThe result will contain that directory (and only that) as its\n \tproject root.  Implies --remap-to-ancestor.\n \n+--notes-filter <command>::\n+\tThis filter rewrites the notes (see linkgit:git-notes[1]).  It\n+\tgets the old notes on standard input, and its standard output\n+\tis written as the new note (overwriting any existing notes).\n++\n+Defaults to doing nothing, unless the `notes.rewrite.filter-branch`\n+option is set, in which case it copies the notes (i.e., defaults to\n+`cat`).\n++\n+In addition to the usual variables, GIT_NEW_COMMIT is set to the\n+result of the rewriting so that the filter can get at the existing\n+notes, if any.\n+\n --remap-to-ancestor::\n \tRewrite refs to the nearest rewritten ancestor instead of\n \tignoring them.\ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex c33a38e..f9252dd 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -350,11 +350,11 @@ rebase::\n filter-branch::\n \tCommits that were processed by 'git-filter-branch', but not\n \tchanged, are not included in the list.  If the list is empty\n-\tafter this filtering, the hook is not invoked at all.\n+\tafter this filtering, the hook is not invoked at all.  Also\n+\tsee the `--post-rewrite` option in\n+\tlinkgit:git-filter-branch[1].\n \n-There is no default 'post-rewrite' hook, but see the\n-`post-receive-copy-notes` script in `contrib/hooks` for an example\n-that copies your git-notes to the rewritten commits.\n+There is no default 'post-rewrite' hook.\n \n \n GIT\ndiff --git a/git-filter-branch.sh b/git-filter-branch.sh\nindex 301c497..ad2da4e 100755\n--- a/git-filter-branch.sh\n+++ b/git-filter-branch.sh\n@@ -101,7 +101,7 @@ USAGE=\"[--env-filter <command>] [--tree-filter <command>]\n             [--index-filter <command>] [--parent-filter <command>]\n             [--msg-filter <command>] [--commit-filter <command>]\n             [--tag-name-filter <command>] [--subdirectory-filter <directory>]\n-\t    [--post-rewrite <command>]\n+\t    [--post-rewrite <command>] [--notes-filter <command>]\n             [--original <namespace>] [-d <directory>] [-f | --force]\n             [<rev-list options>...]\"\n \n@@ -134,6 +134,12 @@ else\n \tpost_rewrite=:\n fi\n \n+if test \"$(git config --bool notes.rewrite.filter-branch)\"; then\n+\tfilter_notes=cat\n+else\n+\tfilter_notes=\n+fi\n+\n while :\n do\n \tcase \"$1\" in\n@@ -198,6 +204,9 @@ do\n \t\tfilter_subdir=\"$OPTARG\"\n \t\tremap_to_ancestor=t\n \t\t;;\n+\t--notes-filter)\n+\t\tfilter_notes=\"$OPTARG\"\n+\t\t;;\n \t--post-rewrite)\n \t\tpost_rewrite=\"$OPTARG\"\n \t\tpost_rewrite_given=t\n@@ -374,6 +383,13 @@ while read commit parents; do\n \tif test $commit != $new_commit; then\n \t\techo $commit $new_commit >> \"$workdir\"/../rewritten\n \tfi\n+\tif test -n \"$filter_notes\"; then\n+\t\tif git notes show $commit >../note 2>/dev/null; then\n+\t\t\texport GIT_NEW_COMMIT=\"$new_commit\"\n+\t\t\teval \"$filter_notes\" <../note |\n+\t\t\tgit notes add -F- -f $new_commit\n+\t\tfi\n+\tfi\n done <../revs\n \n # If we are filtering for paths, as in the case of a subdirectory\ndiff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh\nindex 0da13a8..6dc21e8 100755\n--- a/t/t7003-filter-branch.sh\n+++ b/t/t7003-filter-branch.sh\n@@ -306,6 +306,28 @@ test_expect_success '--remap-to-ancestor with filename filters' '\n \ttest $orig_invariant = $(git rev-parse invariant)\n '\n \n+test_expect_success '--notes-filter off' '\n+\tgit checkout master &&\n+\ttest_commit notes-foo &&\n+\tgit notes add -mtestnote &&\n+\tgit filter-branch -f --tree-filter \"touch notes-1\" HEAD^.. &&\n+\ttest_must_fail git notes show\n+'\n+\n+test_expect_success '--notes-filter manually' '\n+\tgit reset --hard notes-foo &&\n+\tgit filter-branch -f --notes-filter \"sed s/test/foo/\" \\\n+\t\t--tree-filter \"touch notes-2\" HEAD^.. &&\n+\ttest foonote = \"$(git notes show)\"\n+'\n+\n+test_expect_success '--notes-filter implicit' '\n+\tgit reset --hard notes-foo &&\n+\tgit config notes.rewrite.filter-branch true &&\n+\tgit filter-branch -f --tree-filter \"touch notes-3\" HEAD^.. &&\n+\ttest testnote = \"$(git notes show)\"\n+'\n+\n test_expect_success 'setup submodule' '\n \trm -fr ?* .git &&\n \tgit init &&\n-- \n1.7.0.59.g783f8\n"},{"id":"135201","messageId":"eb6ad05f34ee1ad2285320ca2df6bb84e4be2bd5.1266703765.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266703765.git.trast@student.ethz.ch","subject":"[RFC PATCH v3 12/12] notes: add shorthand --ref to override GIT_NOTES_REF","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-20T22:16:33Z","receivedAt":"2010-02-20T22:16:33Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Adds a shorthand option that overrides the GIT_NOTES_REF variable, and\nhence determines the notes tree that will be manipulated.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n Documentation/git-notes.txt |    4 ++++\n builtin-notes.c             |    6 ++++++\n 2 files changed, 10 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex 92f1249..dcdcc95 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -115,6 +115,10 @@ OPTIONS\n \tLike '-C', but with '-c' the editor is invoked, so that\n \tthe user can further edit the note message.\n \n+--ref <ref>::\n+\tManipulate the notes tree in <ref>.  This overrides both\n+\tGIT_NOTES_REF and the \"core.notesRef\" configuration.\n+\n Author\n ------\n Written by Johannes Schindelin <johannes.schindelin@gmx.de> and\ndiff --git a/builtin-notes.c b/builtin-notes.c\nindex 19dd3ab..8cc0037 100644\n--- a/builtin-notes.c\n+++ b/builtin-notes.c\n@@ -430,6 +430,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \tint given_object = 0, i = 1, retval = 0;\n \tstruct msg_arg msg = { 0, 0, STRBUF_INIT };\n \tconst char *rewrite_cmd = NULL;\n+\tconst char *override_notes_ref = NULL;\n \tstruct option options[] = {\n \t\tOPT_GROUP(\"Notes options\"),\n \t\tOPT_CALLBACK('m', \"message\", &msg, \"MSG\",\n@@ -442,6 +443,8 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\t\t   \"reuse specified note object\", parse_reuse_arg),\n \t\tOPT_BOOLEAN('f', \"force\", &force, \"replace existing notes\"),\n \t\tOPT_BOOLEAN(0, \"stdin\", &from_stdin, \"read objects from stdin\"),\n+\t\tOPT_STRING(0, \"ref\", &override_notes_ref, \"notes_ref\",\n+\t\t\t   \"use notes from <notes_ref>\"),\n \t\tOPT_STRING(0, \"for-rewrite\", &rewrite_cmd, \"command\",\n \t\t\t   \"load rewriting config for <command> (implies --stdin)\"),\n \t\tOPT_END()\n@@ -451,6 +454,9 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, options, git_notes_usage, 0);\n \n+\tif (override_notes_ref)\n+\t\tsetenv(\"GIT_NOTES_REF\", override_notes_ref, 1);\n+\n \tif (argc && !strcmp(argv[0], \"list\"))\n \t\tlist = 1;\n \telse if (argc && !strcmp(argv[0], \"add\"))\n-- \n1.7.0.59.g783f8\n"},{"id":"135209","messageId":"7viq9r478z.fsf@alter.siamese.dyndns.org","threadId":"22744","inReplyTo":"34f0e6d201f6bddfc8f4f374493b1b54d60b82cd.1266703765.git.trast@student.ethz.ch","subject":"Re: [RFC PATCH v3 01/12] Support showing notes from more than one notes tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-21T03:06:52Z","receivedAt":"2010-02-21T03:06:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index 4c36aa9..3c5cfec 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -517,7 +517,7 @@ core.notesRef::\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> +appended to the commit message, separated by a \"Notes from <refname>:\" line.  If the\n\nI haven't looked at your code, but I think the output should be kept the\nsame when <refname> is \"refs/notes/commits\", and it probably is a good\nidea to omit \"refs/notes/\" part.  E.g. \"Notes (amlog):\" for notes stored\nin the refs/notes/amlog hierarchy.\n\n> @@ -1300,6 +1300,17 @@ mergetool.keepTemporaries::\n>  mergetool.prompt::\n>  \tPrompt before each invocation of the merge resolution program.\n>  \n> +notes.displayRef::\n> +\tWhen showing commit messages, also show notes which are stored\n> +\tin the given ref.  The ref may also be a glob, in which case\n\nIf this were a description that follows an example\n\n\t[notes]\n\t\tdisplayRef = <ref>\n\nthen the above sentences make sense, but otherwise \"in the given ref\" does\nnot make much sense.  \"The ref may also be a glob\" is a lot worse. A \"ref\"\nis never be a glob.  You can specify what refs to display by setting the\n_value_ of this variable to a glob.\n\n    notes.displayRef::\n        The refname in the notes namespace from which to show notes when\n        showing commit messages.  The value of this variable can be set to\n        a glob, in whch case ...\n\n> +\t...  If a ref\n> +\tdoes not exist, it will be skipped silently.\n\nHmph...  Even when it is not a globbing specification?  If I said\n\n\t[notes]\n\t\tdisplayRef = refs/notes/tr/*\n\nand there is no notes in the tr/ hierarchy yet, I would appreciate if the\ncode doesn't complain, but if I said a more concrete\n\n\t[notes]\n\t\tdisplayRef = refs/notes/anlog\n\nand there is no \"anlog\", because I meant \"amlog\" and the above was a typo,\nI would appreciate if the code somehow lets me know about it.\n\n> +static int notes_display_config(const char *k, const char *v, void *cb)\n> +{\n> +\tif (!strcmp(k, \"notes.displayref\")) {\n> +\t\tstring_list_add_refs_by_glob(&display_notes_refs, v);\n\nSomebody in the callchain should say\n\n\tif (!v)\n        \tconfig_error_nonbool(k)\n\nbefore you use v here.\n\nI don't think string_list_insert() has a desirable property for this.\nIf I wrote\n\n\t[notes]\n            displayRef = refs/notes/commit\n            displayRef = refs/notes/amlog\n\nI would expect that the normal notes come first and then notes from the\namlog tree, not in the alphabetical \"amlog happens to sort before commit\"\norder.  If I used a glob, e.g. \"refs/notes/tr/*\", I am saying \"all notes\nin the tr/ namespace, I do not care about the particular order among them.\nJust use something sensible\", and it would make sense to use some stable\nordering, e.g. the order for_each_glob_ref() calls you back, but receiving\nthe data and calling string_list_insert() will destroy the order the user\ngave you the configuration, no?\n\n> diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\n> index 90178f9..0dfff82 100755\n> --- a/t/t3301-notes.sh\n> +++ b/t/t3301-notes.sh\n> @@ -98,7 +98,7 @@ Date:   Thu Apr 7 15:14:13 2005 -0700\n>  \n>      2nd\n>  \n> -Notes:\n> +Notes from notes/commits:\n\nPlease don't.\n"},{"id":"135215","messageId":"7vd3zz46zm.fsf@alter.siamese.dyndns.org","threadId":"22744","inReplyTo":"99b0d5c4ecb129fd663e50fbb46f41ff9f824c34.1266703765.git.trast@student.ethz.ch","subject":"Re: [RFC PATCH v3 03/12] commit --amend: invoke post-rewrite hook","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-21T03:12:29Z","receivedAt":"2010-02-21T03:12:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> +static const char post_rewrite_hook[] = \"hooks/post-rewrite\";\n> +\n> +static int run_rewrite_hook(const unsigned char *oldsha1,\n> +\t\t\t    const unsigned char *newsha1)\n> +{\n> +...\n> +}\n> +\n>  int cmd_commit(int argc, const char **argv, const char *prefix)\n>  {\n>  \tstruct strbuf sb = STRBUF_INIT;\n> @@ -1303,6 +1339,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n>  \n>  \trerere(0);\n>  \trun_hook(get_index_file(), \"post-commit\", NULL);\n> +\tif (amend && !no_post_rewrite) {\n> +\t\tstruct commit *commit;\n> +\t\tcommit = lookup_commit(head_sha1);\n> +\t\tif (!commit || parse_commit(commit))\n> +\t\t\tdie(\"HEAD commit disappeared right under my eyes?\");\n> +\t\trun_rewrite_hook(commit->object.sha1, commit_sha1);\n\nWhy aren't you passing head_sha1 to run_rewrite_hook() in the first place,\nand instead run lookup_commit() only to get its object.sha1?\n\nThis is merely a \"post\" hook, so failure to run it should not cause the\nmain program to fail.  By not looking it up you do not even have to check\nand call die().  Let the dying be done in the hook instead ;-).\n"},{"id":"135214","messageId":"7v8wan464c.fsf@alter.siamese.dyndns.org","threadId":"22744","inReplyTo":"3559e59dd81bd3fb94196c399e1640681a323147.1266703765.git.trast@student.ethz.ch","subject":"Re: [RFC PATCH v3 06/12] notes: implement 'git notes copy --stdin'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-21T03:31:15Z","receivedAt":"2010-02-21T03:31:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\n> index 0dfff82..634d213 100755\n> --- a/t/t3301-notes.sh\n> +++ b/t/t3301-notes.sh\n> @@ -683,4 +683,36 @@ test_expect_success 'cannot copy note from object without notes' '\n> ...\n> +test_expect_success 'git notes copy --stdin' '\n> +\t(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \\\n> +\techo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |\n> +\tgit notes copy --stdin &&\n> +\tgit log -2 > output &&\n> +\tstrip_then_cmp expect output &&\n\nHuh?\n"},{"id":"135221","messageId":"7v3a0v45yc.fsf@alter.siamese.dyndns.org","threadId":"22744","inReplyTo":"9f4cb8a577e853378c1d3136b54a5e73ac29dc22.1266703765.git.trast@student.ethz.ch","subject":"Re: [RFC PATCH v3 07/12] notes: implement helpers needed for note copying during rewrite","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-21T03:34:51Z","receivedAt":"2010-02-21T03:34:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> +combine_notes_fn *parse_combine_notes_fn(const char *v)\n> +{\n> +\tif (!strcasecmp(v, \"overwrite\"))\n> +\t\treturn combine_notes_overwrite;\n> +\telse if (!strcasecmp(v, \"ignore\"))\n> +\t\treturn combine_notes_ignore;\n> +\telse if (!strcasecmp(v, \"concatenate\"))\n> +\t\treturn combine_notes_concatenate;\n> +\telse\n> +\t\treturn NULL;\n> +}\n\nSomebody in the call chain should have made sure v != NULL and called\nconfig_error_nonbool() if v is NULL.\n\n> diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\n> index 634d213..5a0dccd 100755\n> --- a/t/t3301-notes.sh\n> +++ b/t/t3301-notes.sh\n> @@ -684,6 +684,7 @@ test_expect_success 'cannot copy note from object without notes' '\n>  '\n>  \n>  cat > expect << EOF\n> +commit e5d4fb5698d564ab8c73551538ecaf2b0c666185\n>  Author: A U Thor <author@example.com>\n>  Date:   Thu Apr 7 15:25:13 2005 -0700\n>  \n> @@ -694,6 +695,7 @@ Notes from notes/other:\n>  $whitespace\n>      yet another note\n>  \n> +commit 7038787dfe22a14c3867ce816dbba39845359719\n>  Author: A U Thor <author@example.com>\n>  Date:   Thu Apr 7 15:24:13 2005 -0700\n>  \n> @@ -710,9 +712,123 @@ test_expect_success 'git notes copy --stdin' '\n>  \techo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |\n>  \tgit notes copy --stdin &&\n>  \tgit log -2 > output &&\n> -\tstrip_then_cmp expect output &&\n> +\ttest_cmp expect output &&\n>  \ttest \"$(git notes list HEAD)\" = \"$(git notes list HEAD~2)\" &&\n>  \ttest \"$(git notes list HEAD^)\" = \"$(git notes list HEAD~3)\"\n>  '\n\nI think the above piece should have been squashed to the previous patch.\n"},{"id":"135220","messageId":"7vhbpb2qsm.fsf@alter.siamese.dyndns.org","threadId":"22744","inReplyTo":"cover.1266703765.git.trast@student.ethz.ch","subject":"Re: [RFC PATCH v3 00/12] several notes refs, post-rewrite, notes rewriting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-21T03:47:37Z","receivedAt":"2010-02-21T03:47:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> Brief overview of changes since v2, with some inline questions about\n> things that need to be settled:\n>\n> * new [1/12] \"Support showing notes from more than one notes tree\" that\n>   teaches a \"notes.displayRef\" option which can glob for notes that\n>   should be shown, and can be specified more than once.\n>\n>   - maybe we should default this to \"refs/notes/*\"?\n\nProbably not.\n\nI like the general direction in which this series is going, but a config\nthat does not have any command line counterpart is a design mistake.\n\nWhen adding a feature, always start from the command line option, and then\nadd configuration mechanism purely as a convenience (so that users do not\nhave to give the same option every time they run the command).  By forcing\nyourself to stick to that design pattern, it is easier to ensure that any\nnew feature can be disabled by people who do not want it from the command\nline.\n\n> * new [12/12] \"notes: add shorthand --ref to override GIT_NOTES_REF\"\n\nHmm; if this will _always_ be limited to \"git notes\" command and no other\ncommands will _ever_ learn a command line option to override what notes\nnamespace to use in the future, then \"--ref\" is perfectly a good name.\nBut otherwise, \"git log --ref=notes/amlog\" would look _very wrong_, and\nwould end up being called \"git log --notes-ref=notes/amlog\" instead.  When\nthat happens, \"git notes --ref=notes/amlog\" would look inconsistent and\npeople will complain, no?\n"},{"id":"135223","messageId":"201002210714.02408.trast@student.ethz.ch","threadId":"22744","inReplyTo":"7vhbpb2qsm.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC PATCH v3 00/12] several notes refs, post-rewrite, notes rewriting","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-21T06:14:02Z","receivedAt":"2010-02-21T06:14:02Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"On Sunday 21 February 2010 04:47:37 Junio C Hamano wrote:\n> \n> I like the general direction in which this series is going, but a config\n> that does not have any command line counterpart is a design mistake.\n\nI hate[1] to point out that according to the same criterion, the\n*current* notes are a design mistake.  Unless you count GIT_NOTES_REF,\nin which case in my opinion...\n\n> Hmm; if this will _always_ be limited to \"git notes\" command and no other\n> commands will _ever_ learn a command line option to override what notes\n> namespace to use in the future, then \"--ref\" is perfectly a good name.\n> But otherwise, \"git log --ref=notes/amlog\" would look _very wrong_, and\n> would end up being called \"git log --notes-ref=notes/amlog\" instead.  When\n> that happens, \"git notes --ref=notes/amlog\" would look inconsistent and\n> people will complain, no?\n\n... the obvious extension of the same would be GIT_NOTES_DISPLAY_REF\n(colon separated list of globs), GIT_NOTES_REWRITE_REF,\nGIT_NOTES_REWRITE_MODE, and whatnot.\n\nWhich could still be made into a \"global\" option along the same lines\nas GIT_WORK_TREE.\n\nI don't really want to go the other way and patch every command that\ncould conceivably have notes in its output.\n\n\n[1] No I don't, that just sounded nice.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"135249","messageId":"7vy6imcecn.fsf@alter.siamese.dyndns.org","threadId":"22744","inReplyTo":"201002210714.02408.trast@student.ethz.ch","subject":"Re: [RFC PATCH v3 00/12] several notes refs, post-rewrite, notes rewriting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-22T00:18:32Z","receivedAt":"2010-02-22T00:18:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> On Sunday 21 February 2010 04:47:37 Junio C Hamano wrote:\n>> \n>> I like the general direction in which this series is going, but a config\n>> that does not have any command line counterpart is a design mistake.\n>\n> I hate[1] to point out that according to the same criterion, the\n> *current* notes are a design mistake.  Unless you count GIT_NOTES_REF,\n> in which case in my opinion...\n\nWith \"--no-notes\" the caller can explicitly say \"please do not show any\nnotes at all\" to the log family.  \"git notes\" command itself is about\nmanipluating the notes, and at least with GIT_NOTES_REF the caller can\nexplicitly say what notes tree to work with even when configured value\nis not appropriate.  I also thought there was a work in progress to allow\noverriding GIT_NOTES_REF from the command line?\n\nHaving _only_ configuration option is practically no better than having\nonly hardcoded defaults with _no_ way to override it per invocation.  What\nif a user wants to usually see notes from set A (hence it is configured as\nthe value to display-notes-ref configuration) but wants to see notes from\na different (maybe overlapping, maybe not) set B during another task?  As\nfar as I can see, the _only_ way the current round of RFC lets me do this\nis by resetting the configuration in the repository.\n\nIt is not a contrived example.  I've been adding the info about the\noriginal mailing list message with a patch to commits made with \"am\" in\nnotes/amlog, and would want to keep test result summary in notes/test.\nI'd want to see the test notes just before merging topics to integration\nbranches as a final safety check but not all the time.  I'd want to see\namlog notes when bisect points at a commit in not so near future to be\nable to dig up the archive with, but not all the time.  There needs a\nper-invocation way to specify which sets of notes to show, and a command\nline option is the usual way for commands to allow users to do so [*1*].\n\n> I don't really want to go the other way and patch every command that\n> could conceivably have notes in its output.\n\nIt is Ok that the series may not be perfect during the RFC period (which\nthe patches were clearly marked as), but I've already said \"I like the\ngeneral direction\", wanted to see this series advance from RFC to 'next'\nmaterial sooner rather than later, and was pointing out what needs to\nhappen for that.\n\n\n[Footnote]\n\n*1* We could also use an environment variable, but that is an inferiour\nalternative to options and configuration variables from the point of view\nof UI design.  Users can export GIT_AUTHOR_NAME, which can serve as an\nalternative to user.name.  This can be overridden per-invocation by\n\"GIT_AUTHOR_NAME=myothername git commit\", which can serve as an\nalternative to --author option.\n"},{"id":"135372","messageId":"cover.1266885599.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266703765.git.trast@student.ethz.ch","subject":"[PATCH v5 00/11] several notes refs, post-rewrite, notes rewriting","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T00:42:18Z","receivedAt":"2010-02-23T00:42:18Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"On Tuesday 23 February 2010 00:20:06 Junio C Hamano wrote:\n> Thomas Rast <trast@student.ethz.ch> writes:\n> > diff --git a/notes.c b/notes.c\n[...]\n> > +struct string_list display_notes_refs;\n> > +struct notes_tree **display_notes_trees;\n> \n> Do these need to be extern?\n\nDon't think so?  After all it's in the .c and just another static\nvariable.\n\n> Nice use of strbuf_split().  I wonder if we should automatically add\n> \"refs/\" and/or \"refs/notes/\" when the input is missing the prefix.  I\n> don't have strong preference myself but the users might make noises.\n> \n> Either way it needs to be documented in the final version before the\n> series goes to 'master'.\n\nI decided to do such dwimmery only on the command line, but not for\nthe config.  This is consistent with what's currently happening for\ncore.notesRef.  I also documented this.\n\n> > +static int notes_display_config(const char *k, const char *v, void *cb)\n> > +{\n> > +\t/* Warning!  This is currently not executed if\n> > +\t * GIT_NOTES_DISPLAY_REF is set.  Move the git_config() call\n> > +\t * outside the test if you add more options. */\n> \n> Yuck.  If you know what needs to be done, do that before other poeple add\n> more options, please.\n\n*shrug*\n\nI was trying to be smart and save a call to git_config() when we know\nwe don't care about the config anyway.  After all it does read a bunch\nof files.\n\n> >> > +\tif (flags & NOTES_SHOW_HEADER_WITH_REF && t->ref) {\n[...]\n> >> > +\t} else if (flags & (NOTES_SHOW_HEADER|NOTES_SHOW_HEADER_WITH_REF))\n> >> >  \t\tstrbuf_addstr(sb, \"\\nNotes:\\n\");\n> \n> Meaning nobody will go through the latter \"Notes:\\n\" codepath?  Then what\n> is that else clause for?\n> \n> Perhaps I am not reading your code right in which case this part needs a\n> bit more commenting?\n\nThe catch is in the '&& t->ref'.  Again I'm just trying to be a bit\ndefensive.  Anyway, I ripped out the old formatting and put the NULL\ntest first this time.\n\n> >> I expected to see \"Notes:\\n\" regardless of the mode if the notes is coming\n> >> from the default refs/notes/commits tree, but it probably is better to say\n> >> \"Notes (commits):\\n\" like your patch does.\n> >\n> > I special-cased GIT_NOTES_DEFAULT_REF (which is \"refs/notes/commits\")\n> > above *at your request* to not change the output in the default case.\n> \n> > So which way do you want it?\n> \n> I don't have strong preference anymore with the above code.\n[...]\n> But if everybody calls with HEADER_WITH_REF, no matter what the end user\n> preference is, then it becomes unclear what the right answer would be.\n\nWell, I think we're into bikeshedding territory now, so I just left it\nas it was.\n\n\nThe rest of the changes are listed in each email.\n\n\nThomas Rast (11):\n  test-lib: unset GIT_NOTES_REF to stop it from influencing tests\n  Support showing notes from more than one notes tree\n  Documentation: document post-rewrite hook\n  commit --amend: invoke post-rewrite hook\n  rebase: invoke post-rewrite hook\n  rebase -i: invoke post-rewrite hook\n  notes: implement 'git notes copy --stdin'\n  notes: implement helpers needed for note copying during rewrite\n  rebase: support automatic notes copying\n  commit --amend: copy notes to the new commit\n  notes: add shorthand --ref to override GIT_NOTES_REF\n\n Documentation/config.txt         |   53 +++++-\n Documentation/git-notes.txt      |   21 ++-\n Documentation/githooks.txt       |   38 ++++\n Documentation/pretty-options.txt |   11 +-\n builtin-commit.c                 |   45 +++++\n builtin-log.c                    |    2 +\n builtin-notes.c                  |  195 +++++++++++++++++++-\n builtin.h                        |   18 ++\n cache.h                          |    3 +\n git-am.sh                        |   13 ++\n git-rebase--interactive.sh       |   52 +++++-\n git-rebase.sh                    |    6 +\n notes.c                          |  191 +++++++++++++++++++-\n notes.h                          |   27 +++\n pretty.c                         |    6 +-\n refs.c                           |    4 +-\n refs.h                           |    5 +\n revision.c                       |   21 ++\n revision.h                       |    5 +\n t/t3301-notes.sh                 |  377 +++++++++++++++++++++++++++++++++++++-\n t/t3400-rebase.sh                |   17 ++\n t/t3404-rebase-interactive.sh    |   24 +++\n t/t5407-post-rewrite-hook.sh     |  172 +++++++++++++++++\n t/t7501-commit.sh                |   12 ++\n t/test-lib.sh                    |    4 +\n 25 files changed, 1294 insertions(+), 28 deletions(-)\n create mode 100755 t/t5407-post-rewrite-hook.sh\n"},{"id":"135380","messageId":"b31adf93b35f0003255856c61d002ccf69379c37.1266885599.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266885599.git.trast@student.ethz.ch","subject":"[PATCH v5 01/11] test-lib: unset GIT_NOTES_REF to stop it from influencing tests","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T00:42:19Z","receivedAt":"2010-02-23T00:42:19Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"---\n\n\n t/test-lib.sh |    1 +\n 1 files changed, 1 insertions(+), 0 deletions(-)\n\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex afd3053..1e7eab1 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -54,6 +54,7 @@ unset GIT_OBJECT_DIRECTORY\n unset GIT_CEILING_DIRECTORIES\n unset SHA1_FILE_DIRECTORIES\n unset SHA1_FILE_DIRECTORY\n+unset GIT_NOTES_REF\n GIT_MERGE_VERBOSITY=5\n export GIT_MERGE_VERBOSITY\n export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME\n-- \n1.7.0.218.g73a398\n"},{"id":"135373","messageId":"3dbcdcf1a364d14968c07e99564acb232c6a5c43.1266885599.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266885599.git.trast@student.ethz.ch","subject":"[PATCH v5 02/11] Support showing notes from more than one notes tree","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T00:42:20Z","receivedAt":"2010-02-23T00:42:20Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"With this patch, you can set notes.displayRef to a glob that points at\nyour favourite notes refs, e.g.,\n\n[notes]\n\tdisplayRef = refs/notes/*\n\nThen git-log and friends will show notes from all trees.\n\nThanks to Junio C Hamano for lots of feedback, which greatly\ninfluenced the design of the entire series and this commit in\nparticular.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nChanges since v4:\n* introduce --show-notes=<ref> and --[no-]standard-notes\n* remove NOTES_SHOW_HEADER_WITH_REF distinction\n* parse config even if we don't care about it\n* fix misallocation in load_notes_trees()\n* doc tweaks\n\n Documentation/config.txt         |   23 +++++-\n Documentation/pretty-options.txt |   11 ++-\n builtin-log.c                    |    2 +\n cache.h                          |    1 +\n notes.c                          |  171 ++++++++++++++++++++++++++++++++++++--\n notes.h                          |   18 ++++\n pretty.c                         |    6 +-\n refs.c                           |    4 +-\n refs.h                           |    5 +\n revision.c                       |   21 +++++\n revision.h                       |    5 +\n t/t3301-notes.sh                 |  148 +++++++++++++++++++++++++++++++--\n t/test-lib.sh                    |    1 +\n 13 files changed, 392 insertions(+), 24 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 664de6b..e077445 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -519,10 +519,12 @@ check that makes sure that existing object files will not get overwritten.\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+\tafter the full SHA-1 of the commit they annotate.  The ref\n+\tmust be fully qualified.\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+appended to the commit message, separated by a \"Notes (<refname>):\"\n+line (shortened to \"Notes:\" in the case of \"refs/notes/commits\").  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@@ -1305,6 +1307,23 @@ mergetool.keepTemporaries::\n mergetool.prompt::\n \tPrompt before each invocation of the merge resolution program.\n \n+notes.displayRef::\n+\tThe (fully qualified) refname from which to show notes when\n+\tshowing commit messages.  The value of this variable can be set\n+\tto a glob, in which case notes from all matching refs will be\n+\tshown.  You may also specify this configuration variable\n+\tseveral times.  A warning will be issued for refs that do not\n+\texist, but a glob that does not match any refs is silently\n+\tignored.\n++\n+This setting can be overridden with the `GIT_NOTES_DISPLAY_REF`\n+environment variable, which must be a colon separated list of refs or\n+globs.\n++\n+The effective value of \"core.notesRef\" (possibly overridden by\n+GIT_NOTES_REF) is also implicitly added to the list of refs to be\n+displayed.\n+\n pack.window::\n \tThe size of the window used by linkgit:git-pack-objects[1] when no\n \twindow size is given on the command line. Defaults to 10.\ndiff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt\nindex aa96cae..1d56926 100644\n--- a/Documentation/pretty-options.txt\n+++ b/Documentation/pretty-options.txt\n@@ -30,9 +30,18 @@ people using 80-column terminals.\n \tdefaults to UTF-8.\n \n --no-notes::\n---show-notes::\n+--show-notes[=<ref>]::\n \tShow the notes (see linkgit:git-notes[1]) that annotate the\n \tcommit, when showing the commit log message.  This is the default\n \tfor `git log`, `git show` and `git whatchanged` commands when\n \tthere is no `--pretty`, `--format` nor `--oneline` option is\n \tgiven on the command line.\n++\n+With an optional argument, add this ref to the list of notes.  The ref\n+is taken to be in `refs/notes/` if it is not qualified.\n+\n+--[no-]standard-notes::\n+\tEnable or disable populating the notes ref list from the\n+\t'core.notesRef' and 'notes.displayRef' variables (or\n+\tcorresponding environment overrides).  See\n+\tlinkgit:git-config[1].\ndiff --git a/builtin-log.c b/builtin-log.c\nindex e0d5caa..e7ea088 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -60,6 +60,8 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n \n \tif (!rev->show_notes_given && !rev->pretty_given)\n \t\trev->show_notes = 1;\n+\tif (rev->show_notes)\n+\t\tinit_display_notes(&rev->notes_opt);\n \n \tif (rev->diffopt.pickaxe || rev->diffopt.filter)\n \t\trev->always_show_header = 0;\ndiff --git a/cache.h b/cache.h\nindex c863085..ee4ae3e 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -387,6 +387,7 @@ static inline enum object_type object_type(unsigned int mode)\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+#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT \"GIT_NOTES_DISPLAY_REF\"\n \n extern int is_bare_repository_cfg;\n extern int is_bare_repository(void);\ndiff --git a/notes.c b/notes.c\nindex 3ba3e6d..ee54a42 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -5,6 +5,8 @@\n #include \"utf8.h\"\n #include \"strbuf.h\"\n #include \"tree-walk.h\"\n+#include \"string-list.h\"\n+#include \"refs.h\"\n \n /*\n  * Use a non-balancing simple 16-tree structure with struct int_node as\n@@ -68,6 +70,9 @@ struct non_note {\n \n struct notes_tree default_notes_tree;\n \n+struct string_list display_notes_refs;\n+struct notes_tree **display_notes_trees;\n+\n static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\tstruct int_node *node, unsigned int n);\n \n@@ -828,6 +833,76 @@ int combine_notes_ignore(unsigned char *cur_sha1,\n \treturn 0;\n }\n \n+static int string_list_add_one_ref(const char *path, const unsigned char *sha1,\n+\t\t\t\t   int flag, void *cb)\n+{\n+\tstruct string_list *refs = cb;\n+\tif (!unsorted_string_list_has_string(refs, path))\n+\t\tstring_list_append(path, refs);\n+\treturn 0;\n+}\n+\n+void string_list_add_refs_by_glob(struct string_list *list, const char *glob)\n+{\n+\tif (has_glob_specials(glob)) {\n+\t\tfor_each_glob_ref(string_list_add_one_ref, glob, list);\n+\t} else {\n+\t\tunsigned char sha1[20];\n+\t\tif (get_sha1(glob, sha1))\n+\t\t\twarning(\"notes ref %s is invalid\", glob);\n+\t\tif (!unsorted_string_list_has_string(list, glob))\n+\t\t\tstring_list_append(glob, list);\n+\t}\n+}\n+\n+void string_list_add_refs_from_colon_sep(struct string_list *list,\n+\t\t\t\t\t const char *globs)\n+{\n+\tstruct strbuf globbuf = STRBUF_INIT;\n+\tstruct strbuf **split;\n+\tint i;\n+\n+\tstrbuf_addstr(&globbuf, globs);\n+\tsplit = strbuf_split(&globbuf, ':');\n+\n+\tfor (i = 0; split[i]; i++) {\n+\t\tif (!split[i]->len)\n+\t\t\tcontinue;\n+\t\tif (split[i]->buf[split[i]->len-1] == ':')\n+\t\t\tstrbuf_setlen(split[i], split[i]->len-1);\n+\t\tstring_list_add_refs_by_glob(list, split[i]->buf);\n+\t}\n+\n+\tstrbuf_list_free(split);\n+\tstrbuf_release(&globbuf);\n+}\n+\n+static int notes_display_config(const char *k, const char *v, void *cb)\n+{\n+\tint *load_refs = cb;\n+\n+\tif (*load_refs && !strcmp(k, \"notes.displayref\")) {\n+\t\tif (!v)\n+\t\t\tconfig_error_nonbool(k);\n+\t\tstring_list_add_refs_by_glob(&display_notes_refs, v);\n+\t\treturn 0;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static const char *default_notes_ref()\n+{\n+\tconst char *notes_ref = NULL;\n+\tif (!notes_ref)\n+\t\tnotes_ref = getenv(GIT_NOTES_REF_ENVIRONMENT);\n+\tif (!notes_ref)\n+\t\tnotes_ref = notes_ref_name; /* value of core.notesRef config */\n+\tif (!notes_ref)\n+\t\tnotes_ref = GIT_NOTES_DEFAULT_REF;\n+\treturn notes_ref;\n+}\n+\n void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\tcombine_notes_fn combine_notes, int flags)\n {\n@@ -840,11 +915,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \tassert(!t->initialized);\n \n \tif (!notes_ref)\n-\t\tnotes_ref = getenv(GIT_NOTES_REF_ENVIRONMENT);\n-\tif (!notes_ref)\n-\t\tnotes_ref = notes_ref_name; /* value of core.notesRef config */\n-\tif (!notes_ref)\n-\t\tnotes_ref = GIT_NOTES_DEFAULT_REF;\n+\t\tnotes_ref = default_notes_ref();\n \n \tif (!combine_notes)\n \t\tcombine_notes = combine_notes_concatenate;\n@@ -868,6 +939,71 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \tload_subtree(t, &root_tree, t->root, 0);\n }\n \n+struct load_notes_cb_data\n+{\n+\tint counter;\n+\tstruct notes_tree **trees;\n+};\n+\n+static int load_one_display_note_ref(struct string_list_item *item,\n+\t\t\t\t     void *cb_data)\n+{\n+\tstruct load_notes_cb_data *c = cb_data;\n+\tstruct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));\n+\tinit_notes(t, item->string, combine_notes_ignore, 0);\n+\tc->trees[c->counter++] = t;\n+\treturn 0;\n+}\n+\n+struct notes_tree **load_notes_trees(struct string_list *refs)\n+{\n+\tstruct notes_tree **trees;\n+\tstruct load_notes_cb_data cb_data;\n+\ttrees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *));\n+\tcb_data.counter = 0;\n+\tcb_data.trees = trees;\n+\tfor_each_string_list(load_one_display_note_ref, refs, &cb_data);\n+\ttrees[cb_data.counter] = NULL;\n+\treturn trees;\n+}\n+\n+static int string_list_add_refs_from_list(struct string_list_item *item,\n+\t\t\t\t\t  void *cb)\n+{\n+\tstruct string_list *list = cb;\n+\tstring_list_add_refs_by_glob(list, item->string);\n+\treturn 0;\n+}\n+\n+void init_display_notes(struct display_notes_opt *opt)\n+{\n+\tchar *display_ref_env;\n+\tint load_config_refs = 0;\n+\tdisplay_notes_refs.strdup_strings = 1;\n+\n+\tif (!opt || !opt->suppress_default_notes) {\n+\t\tstring_list_append(default_notes_ref(), &display_notes_refs);\n+\t\tdisplay_ref_env = getenv(GIT_NOTES_DISPLAY_REF_ENVIRONMENT);\n+\t\tif (display_ref_env) {\n+\t\t\tstring_list_add_refs_from_colon_sep(&display_notes_refs,\n+\t\t\t\t\t\t\t    display_ref_env);\n+\t\t\tload_config_refs = 0;\n+\t\t} else {\n+\t\t\tload_config_refs = 1;\n+\t\t}\n+\t}\n+\n+\tgit_config(notes_display_config, &load_config_refs);\n+\n+\tif (opt && opt->extra_notes_refs)\n+\t\tfor_each_string_list(string_list_add_refs_from_list,\n+\t\t\t\t     opt->extra_notes_refs,\n+\t\t\t\t     &display_notes_refs);\n+\n+\tdisplay_notes_trees = load_notes_trees(&display_notes_refs);\n+\tstring_list_clear(&display_notes_refs, 0);\n+}\n+\n void add_note(struct notes_tree *t, const unsigned char *object_sha1,\n \t\tconst unsigned char *note_sha1, combine_notes_fn combine_notes)\n {\n@@ -1016,8 +1152,18 @@ void format_note(struct notes_tree *t, const unsigned char *object_sha1,\n \tif (msglen && msg[msglen - 1] == '\\n')\n \t\tmsglen--;\n \n-\tif (flags & NOTES_SHOW_HEADER)\n-\t\tstrbuf_addstr(sb, \"\\nNotes:\\n\");\n+\tif (flags & NOTES_SHOW_HEADER) {\n+\t\tconst char *ref = t->ref;\n+\t\tif (!ref || !strcmp(ref, GIT_NOTES_DEFAULT_REF)) {\n+\t\t\tstrbuf_addstr(sb, \"\\nNotes:\\n\");\n+\t\t} else {\n+\t\t\tif (!prefixcmp(ref, \"refs/\"))\n+\t\t\t\tref += 5;\n+\t\t\tif (!prefixcmp(ref, \"notes/\"))\n+\t\t\t\tref += 6;\n+\t\t\tstrbuf_addf(sb, \"\\nNotes (%s):\\n\", ref);\n+\t\t}\n+\t}\n \n \tfor (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {\n \t\tlinelen = strchrnul(msg_p, '\\n') - msg_p;\n@@ -1030,3 +1176,14 @@ void format_note(struct notes_tree *t, const unsigned char *object_sha1,\n \n \tfree(msg);\n }\n+\n+void format_display_notes(const unsigned char *object_sha1,\n+\t\t\t  struct strbuf *sb, const char *output_encoding, int flags)\n+{\n+\tint i;\n+\tif (!display_notes_trees)\n+\t\tinit_display_notes(NULL);\n+\tfor (i = 0; display_notes_trees[i]; i++)\n+\t\tformat_note(display_notes_trees[i], object_sha1, sb,\n+\t\t\t    output_encoding, flags);\n+}\ndiff --git a/notes.h b/notes.h\nindex bad03cc..7650254 100644\n--- a/notes.h\n+++ b/notes.h\n@@ -198,4 +198,22 @@ int for_each_note(struct notes_tree *t, int flags, each_note_fn fn,\n void format_note(struct notes_tree *t, const unsigned char *object_sha1,\n \t\tstruct strbuf *sb, const char *output_encoding, int flags);\n \n+\n+struct string_list;\n+\n+struct display_notes_opt\n+{\n+\tint suppress_default_notes : 1;\n+\tstruct string_list *extra_notes_refs;\n+};\n+\n+void init_display_notes(struct display_notes_opt *opt);\n+void format_display_notes(const unsigned char *object_sha1,\n+\t\t\t  struct strbuf *sb, const char *output_encoding, int flags);\n+\n+struct notes_tree **load_notes_trees(struct string_list *refs);\n+void string_list_add_refs_by_glob(struct string_list *list, const char *glob);\n+void string_list_add_refs_from_colon_sep(struct string_list *list,\n+\t\t\t\t\t const char *globs);\n+\n #endif\ndiff --git a/pretty.c b/pretty.c\nindex f999485..c185fd2 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -775,7 +775,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\t}\n \t\treturn 0;\t/* unknown %g placeholder */\n \tcase 'N':\n-\t\tformat_note(NULL, commit->object.sha1, sb,\n+\t\tformat_display_notes(commit->object.sha1, sb,\n \t\t\t    git_log_output_encoding ? git_log_output_encoding\n \t\t\t\t\t\t    : git_commit_encoding, 0);\n \t\treturn 1;\n@@ -1096,8 +1096,8 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,\n \t\tstrbuf_addch(sb, '\\n');\n \n \tif (context->show_notes)\n-\t\tformat_note(NULL, commit->object.sha1, sb, encoding,\n-\t\t\t    NOTES_SHOW_HEADER | NOTES_INDENT);\n+\t\tformat_display_notes(commit->object.sha1, sb, encoding,\n+\t\t\t\t     NOTES_SHOW_HEADER_WITH_REF | NOTES_INDENT);\n \n \tfree(reencoded);\n }\ndiff --git a/refs.c b/refs.c\nindex f3fcbe0..5a860c4 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -695,7 +695,6 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,\n {\n \tstruct strbuf real_pattern = STRBUF_INIT;\n \tstruct ref_filter filter;\n-\tconst char *has_glob_specials;\n \tint ret;\n \n \tif (!prefix && prefixcmp(pattern, \"refs/\"))\n@@ -704,8 +703,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,\n \t\tstrbuf_addstr(&real_pattern, prefix);\n \tstrbuf_addstr(&real_pattern, pattern);\n \n-\thas_glob_specials = strpbrk(pattern, \"?*[\");\n-\tif (!has_glob_specials) {\n+\tif (!has_glob_specials(pattern)) {\n \t\t/* Append implied '/' '*' if not present. */\n \t\tif (real_pattern.buf[real_pattern.len - 1] != '/')\n \t\t\tstrbuf_addch(&real_pattern, '/');\ndiff --git a/refs.h b/refs.h\nindex f7648b9..4a18b08 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -28,6 +28,11 @@ struct ref_lock {\n extern int for_each_glob_ref(each_ref_fn, const char *pattern, void *);\n extern int for_each_glob_ref_in(each_ref_fn, const char *pattern, const char* prefix, void *);\n \n+static inline const char *has_glob_specials(const char *pattern)\n+{\n+\treturn strpbrk(pattern, \"?*[\");\n+}\n+\n /* can be used to learn about broken ref and symref */\n extern int for_each_rawref(each_ref_fn, void *);\n \ndiff --git a/revision.c b/revision.c\nindex 29721ec..d6e842e 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -12,6 +12,7 @@\n #include \"patch-ids.h\"\n #include \"decorate.h\"\n #include \"log-tree.h\"\n+#include \"string-list.h\"\n \n volatile show_early_output_fn_t show_early_output;\n \n@@ -1191,9 +1192,29 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t} else if (!strcmp(arg, \"--show-notes\")) {\n \t\trevs->show_notes = 1;\n \t\trevs->show_notes_given = 1;\n+\t} else if (!prefixcmp(arg, \"--show-notes=\")) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\trevs->show_notes = 1;\n+\t\trevs->show_notes_given = 1;\n+\t\tif (!revs->notes_opt.extra_notes_refs)\n+\t\t\trevs->notes_opt.extra_notes_refs = xcalloc(1, sizeof(struct string_list));\n+\t\tif (!prefixcmp(arg+13, \"refs/\"))\n+\t\t\t/* happy */;\n+\t\telse if (!prefixcmp(arg+13, \"notes/\"))\n+\t\t\tstrbuf_addstr(&buf, \"refs/\");\n+\t\telse\n+\t\t\tstrbuf_addstr(&buf, \"refs/notes/\");\n+\t\tstrbuf_addstr(&buf, arg+13);\n+\t\tstring_list_append(strbuf_detach(&buf, NULL),\n+\t\t\t\t   revs->notes_opt.extra_notes_refs);\n \t} else if (!strcmp(arg, \"--no-notes\")) {\n \t\trevs->show_notes = 0;\n \t\trevs->show_notes_given = 1;\n+\t} else if (!strcmp(arg, \"--standard-notes\")) {\n+\t\trevs->show_notes_given = 1;\n+\t\trevs->notes_opt.suppress_default_notes = 0;\n+\t} else if (!strcmp(arg, \"--no-standard-notes\")) {\n+\t\trevs->notes_opt.suppress_default_notes = 1;\n \t} else if (!strcmp(arg, \"--oneline\")) {\n \t\trevs->verbose_header = 1;\n \t\tget_commit_format(\"oneline\", revs);\ndiff --git a/revision.h b/revision.h\nindex a14deef..580f6ec 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -3,6 +3,7 @@\n \n #include \"parse-options.h\"\n #include \"grep.h\"\n+#include \"notes.h\"\n \n #define SEEN\t\t(1u<<0)\n #define UNINTERESTING   (1u<<1)\n@@ -20,6 +21,7 @@\n \n struct rev_info;\n struct log_info;\n+struct string_list;\n \n struct rev_info {\n \t/* Starting list */\n@@ -126,6 +128,9 @@ struct rev_info {\n \tstruct reflog_walk_info *reflog_info;\n \tstruct decoration children;\n \tstruct decoration merge_simplification;\n+\n+\t/* notes-specific options: which refs to show */\n+\tstruct display_notes_opt notes_opt;\n };\n \n #define REV_TREE_SAME\t\t0\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex 90178f9..cb7166f 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -415,7 +415,7 @@ Date:   Thu Apr 7 15:18:13 2005 -0700\n \n     6th\n \n-Notes:\n+Notes (other):\n     other note\n EOF\n \n@@ -448,7 +448,139 @@ test_expect_success 'Do not show note when core.notesRef is overridden' '\n \ttest_cmp expect-not-other output\n '\n \n+cat > expect-both << EOF\n+commit 387a89921c73d7ed72cd94d179c1c7048ca47756\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:18:13 2005 -0700\n+\n+    6th\n+\n+Notes:\n+    order test\n+\n+Notes (other):\n+    other note\n+\n+commit bd1753200303d0a0344be813e504253b3d98e74d\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:17:13 2005 -0700\n+\n+    5th\n+\n+Notes:\n+    replacement for deleted note\n+EOF\n+\n+test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '\n+\tGIT_NOTES_REF=refs/notes/commits git notes add \\\n+\t\t-m\"replacement for deleted note\" HEAD^ &&\n+\tGIT_NOTES_REF=refs/notes/commits git notes add -m\"order test\" &&\n+\tgit config --unset core.notesRef &&\n+\tgit config notes.displayRef \"refs/notes/*\" &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect-both output\n+'\n+\n+test_expect_success 'core.notesRef is implicitly in notes.displayRef' '\n+\tgit config core.notesRef refs/notes/commits &&\n+\tgit config notes.displayRef refs/notes/other &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect-both output\n+'\n+\n+test_expect_success 'notes.displayRef can be given more than once' '\n+\tgit config --unset core.notesRef &&\n+\tgit config notes.displayRef refs/notes/commits &&\n+\tgit config --add notes.displayRef refs/notes/other &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect-both output\n+'\n+\n+cat > expect-both-reversed << EOF\n+commit 387a89921c73d7ed72cd94d179c1c7048ca47756\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:18:13 2005 -0700\n+\n+    6th\n+\n+Notes (other):\n+    other note\n+\n+Notes:\n+    order test\n+EOF\n+\n+test_expect_success 'notes.displayRef respects order' '\n+\tgit config core.notesRef refs/notes/other &&\n+\tgit config --unset-all notes.displayRef &&\n+\tgit config notes.displayRef refs/notes/commits &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect-both-reversed output\n+'\n+\n+test_expect_success 'GIT_NOTES_DISPLAY_REF works' '\n+\tgit config --unset-all core.notesRef &&\n+\tgit config --unset-all notes.displayRef &&\n+\tGIT_NOTES_DISPLAY_REF=refs/notes/commits:refs/notes/other \\\n+\t\tgit log -2 > output &&\n+\ttest_cmp expect-both output\n+'\n+\n+cat > expect-none << EOF\n+commit 387a89921c73d7ed72cd94d179c1c7048ca47756\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:18:13 2005 -0700\n+\n+    6th\n+\n+commit bd1753200303d0a0344be813e504253b3d98e74d\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:17:13 2005 -0700\n+\n+    5th\n+EOF\n+\n+test_expect_success 'GIT_NOTES_DISPLAY_REF overrides config' '\n+\tgit config notes.displayRef \"refs/notes/*\" &&\n+\tGIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log -2 > output &&\n+\ttest_cmp expect-none output\n+'\n+\n+test_expect_success '--show-notes=* adds to GIT_NOTES_DISPLAY_REF' '\n+\tGIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log --show-notes=* -2 > output &&\n+\ttest_cmp expect-both output\n+'\n+\n+cat > expect-commits << EOF\n+commit 387a89921c73d7ed72cd94d179c1c7048ca47756\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:18:13 2005 -0700\n+\n+    6th\n+\n+Notes:\n+    order test\n+EOF\n+\n+test_expect_success '--no-standard-notes' '\n+\tgit log --no-standard-notes --show-notes=commits -1 > output &&\n+\ttest_cmp expect-commits output\n+'\n+\n+test_expect_success '--standard-notes' '\n+\tgit log --no-standard-notes --show-notes=commits \\\n+\t\t--standard-notes -2 > output &&\n+\ttest_cmp expect-both output\n+'\n+\n+test_expect_success '--show-notes=ref accumulates' '\n+\tgit log --show-notes=other --show-notes=commits \\\n+\t\t --no-standard-notes -1 > output &&\n+\ttest_cmp expect-both-reversed output\n+'\n+\n test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '\n+\tgit config core.notesRef refs/notes/other &&\n \techo \"Note on a tree\" > expect\n \tgit notes add -m \"Note on a tree\" HEAD: &&\n \tgit notes show HEAD: > actual &&\n@@ -472,7 +604,7 @@ Date:   Thu Apr 7 15:19:13 2005 -0700\n \n     7th\n \n-Notes:\n+Notes (other):\n     other note\n EOF\n \n@@ -503,7 +635,7 @@ Date:   Thu Apr 7 15:21:13 2005 -0700\n \n     9th\n \n-Notes:\n+Notes (other):\n     yet another note\n EOF\n \n@@ -533,7 +665,7 @@ Date:   Thu Apr 7 15:21:13 2005 -0700\n \n     9th\n \n-Notes:\n+Notes (other):\n     yet another note\n $whitespace\n     yet another note\n@@ -552,7 +684,7 @@ Date:   Thu Apr 7 15:22:13 2005 -0700\n \n     10th\n \n-Notes:\n+Notes (other):\n     other note\n EOF\n \n@@ -569,7 +701,7 @@ Date:   Thu Apr 7 15:22:13 2005 -0700\n \n     10th\n \n-Notes:\n+Notes (other):\n     other note\n $whitespace\n     yet another note\n@@ -588,7 +720,7 @@ Date:   Thu Apr 7 15:23:13 2005 -0700\n \n     11th\n \n-Notes:\n+Notes (other):\n     other note\n $whitespace\n     yet another note\n@@ -619,7 +751,7 @@ Date:   Thu Apr 7 15:23:13 2005 -0700\n \n     11th\n \n-Notes:\n+Notes (other):\n     yet another note\n $whitespace\n     yet another note\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 1e7eab1..c814e9c 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -55,6 +55,7 @@ unset GIT_CEILING_DIRECTORIES\n unset SHA1_FILE_DIRECTORIES\n unset SHA1_FILE_DIRECTORY\n unset GIT_NOTES_REF\n+unset GIT_NOTES_DISPLAY_REF\n GIT_MERGE_VERBOSITY=5\n export GIT_MERGE_VERBOSITY\n export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME\n-- \n1.7.0.218.g73a398\n"},{"id":"135374","messageId":"3ecc609fab67bb7c5dfbad097c469056148fab53.1266885599.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266885599.git.trast@student.ethz.ch","subject":"[PATCH v5 03/11] Documentation: document post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T00:42:21Z","receivedAt":"2010-02-23T00:42:21Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"This defines the behaviour of the post-rewrite hook support, which\nwill be implemented in the following patches.\n\nWe deliberately do not document how often the hook will be invoked per\nrewriting command, but the interface is designed to keep that at\n\"once\".  This would currently not matter too much, since both rebase\nand filter-branch are shellscripts and spawn many processes anyway.\nHowever, when a fast sequencer in C is implemented, it will be\nbeneficial to only have to run the hook once.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\n* adapt docs to the fact that git-filter-branch doesn't call the hook\n  in this series\n* specify commit ordering for rebase\n\n Documentation/githooks.txt |   34 ++++++++++++++++++++++++++++++++++\n 1 files changed, 34 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex 87e2c03..a741769 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -317,6 +317,40 @@ This hook is invoked by 'git gc --auto'. It takes no parameter, and\n exiting with non-zero status from this script causes the 'git gc --auto'\n to abort.\n \n+post-rewrite\n+~~~~~~~~~~~~\n+\n+This hook is invoked by commands that rewrite commits (`git commit\n+--amend`, 'git-rebase'; currently 'git-filter-branch' does 'not' call\n+it!).  Its first argument denotes the command it was invoked by:\n+currently one of `amend` or `rebase`.  Further command-dependent\n+arguments may be passed in the future.\n+\n+The hook receives a list of the rewritten commits on stdin, in the\n+format\n+\n+  <old-sha1> SP <new-sha1> [ SP <extra-info> ] LF\n+\n+The 'extra-info' is again command-dependent.  If it is empty, the\n+preceding SP is also omitted.  Currently, no commands pass any\n+'extra-info'.\n+\n+The following command-specific comments apply:\n+\n+rebase::\n+\tFor the 'squash' and 'fixup' operation, all commits that were\n+\tsquashed are listed as being rewritten to the squashed commit.\n+\tThis means that there will be several lines sharing the same\n+\t'new-sha1'.\n++\n+The commits are guaranteed to be listed in the order that they were\n+processed by rebase.\n+\n+There is no default 'post-rewrite' hook, but see the\n+`post-receive-copy-notes` script in `contrib/hooks` for an example\n+that copies your git-notes to the rewritten commits.\n+\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \n1.7.0.218.g73a398\n"},{"id":"135376","messageId":"96e8531bd90e4b73ffe47cf28a66073abad97ecc.1266885599.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266885599.git.trast@student.ethz.ch","subject":"[PATCH v5 04/11] commit --amend: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T00:42:22Z","receivedAt":"2010-02-23T00:42:22Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"The rough structure of run_rewrite_hook() comes from\nrun_receive_hook() in receive-pack.\n\nWe introduce a --no-post-rewrite option and use it to avoid the hook\nwhen called from git-rebase -i 'edit'.  The next patch will add full\nsupport in git-rebase, and we only want to invoke the hook once.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\n\n builtin-commit.c             |   39 +++++++++++++++++++++++++++++++\n git-rebase--interactive.sh   |    2 +-\n t/t5407-post-rewrite-hook.sh |   52 ++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 92 insertions(+), 1 deletions(-)\n create mode 100755 t/t5407-post-rewrite-hook.sh\n\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex bb97000..1c3aa7d 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -66,6 +66,7 @@\n static char *author_name, *author_email, *author_date;\n static int all, edit_flag, also, interactive, only, amend, signoff;\n static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;\n+static int no_post_rewrite;\n static char *untracked_files_arg, *force_date;\n /*\n  * The default commit message cleanup mode will remove the lines\n@@ -137,6 +138,7 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset)\n \tOPT_BOOLEAN('z', \"null\", &null_termination,\n \t\t    \"terminate entries with NUL\"),\n \tOPT_BOOLEAN(0, \"amend\", &amend, \"amend previous commit\"),\n+\tOPT_BOOLEAN(0, \"no-post-rewrite\", &no_post_rewrite, \"bypass post-rewrite hook\"),\n \t{ OPTION_STRING, 'u', \"untracked-files\", &untracked_files_arg, \"mode\", \"show untracked files, optional modes: all, normal, no. (Default: all)\", PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n \tOPT_BOOLEAN(0, \"allow-empty\", &allow_empty, \"ok to record an empty change\"),\n \t/* end commit contents options */\n@@ -1160,6 +1162,40 @@ static int git_commit_config(const char *k, const char *v, void *cb)\n \treturn git_status_config(k, v, s);\n }\n \n+static const char post_rewrite_hook[] = \"hooks/post-rewrite\";\n+\n+static int run_rewrite_hook(const unsigned char *oldsha1,\n+\t\t\t    const unsigned char *newsha1)\n+{\n+\t/* oldsha1 SP newsha1 LF NUL */\n+\tstatic char buf[2*40 + 3];\n+\tstruct child_process proc;\n+\tconst char *argv[3];\n+\tint code;\n+\tsize_t n;\n+\n+\tif (access(git_path(post_rewrite_hook), X_OK) < 0)\n+\t\treturn 0;\n+\n+\targv[0] = git_path(post_rewrite_hook);\n+\targv[1] = \"amend\";\n+\targv[2] = NULL;\n+\n+\tmemset(&proc, 0, sizeof(proc));\n+\tproc.argv = argv;\n+\tproc.in = -1;\n+\tproc.stdout_to_stderr = 1;\n+\n+\tcode = start_command(&proc);\n+\tif (code)\n+\t\treturn code;\n+\tn = snprintf(buf, sizeof(buf), \"%s %s\\n\",\n+\t\t     sha1_to_hex(oldsha1), sha1_to_hex(newsha1));\n+\twrite_in_full(proc.in, buf, n);\n+\tclose(proc.in);\n+\treturn finish_command(&proc);\n+}\n+\n int cmd_commit(int argc, const char **argv, const char *prefix)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -1303,6 +1339,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \n \trerere(0);\n \trun_hook(get_index_file(), \"post-commit\", NULL);\n+\tif (amend && !no_post_rewrite) {\n+\t\trun_rewrite_hook(head_sha1, commit_sha1);\n+\t}\n \tif (!quiet)\n \t\tprint_summary(prefix, commit_sha1);\n \ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex 3e4fd14..5735859 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -445,7 +445,7 @@ do_next () {\n \t\tmark_action_done\n \t\tpick_one $sha1 ||\n \t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n-\t\tgit commit --amend\n+\t\tgit commit --amend --no-post-rewrite\n \t\t;;\n \tedit|e)\n \t\tcomment_for_reflog edit\ndiff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh\nnew file mode 100755\nindex 0000000..1020af9\n--- /dev/null\n+++ b/t/t5407-post-rewrite-hook.sh\n@@ -0,0 +1,52 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2010 Thomas Rast\n+#\n+\n+test_description='Test the post-rewrite hook.'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit A foo A &&\n+\ttest_commit B foo B &&\n+\ttest_commit C foo C &&\n+\ttest_commit D foo D\n+'\n+\n+mkdir .git/hooks\n+\n+cat >.git/hooks/post-rewrite <<EOF\n+#!/bin/sh\n+echo \\$@ > \"$TRASH_DIRECTORY\"/post-rewrite.args\n+cat > \"$TRASH_DIRECTORY\"/post-rewrite.data\n+EOF\n+chmod u+x .git/hooks/post-rewrite\n+\n+clear_hook_input () {\n+\trm -f post-rewrite.args post-rewrite.data\n+}\n+\n+verify_hook_input () {\n+\ttest_cmp \"$TRASH_DIRECTORY\"/post-rewrite.args expected.args &&\n+\ttest_cmp \"$TRASH_DIRECTORY\"/post-rewrite.data expected.data\n+}\n+\n+test_expect_success 'git commit --amend' '\n+\tclear_hook_input &&\n+\techo \"D new message\" > newmsg &&\n+\toldsha=$(git rev-parse HEAD^0) &&\n+\tgit commit -Fnewmsg --amend &&\n+\techo amend > expected.args &&\n+\techo $oldsha $(git rev-parse HEAD^0) > expected.data &&\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git commit --amend --no-post-rewrite' '\n+\tclear_hook_input &&\n+\techo \"D new message again\" > newmsg &&\n+\tgit commit --no-post-rewrite -Fnewmsg --amend &&\n+\ttest ! -f post-rewrite.args &&\n+\ttest ! -f post-rewrite.data\n+'\n+\n+test_done\n-- \n1.7.0.218.g73a398\n"},{"id":"135375","messageId":"93cf3e73564e068b7aaf5a43a54ffd02ef6b2e91.1266885599.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266885599.git.trast@student.ethz.ch","subject":"[PATCH v5 05/11] rebase: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T00:42:23Z","receivedAt":"2010-02-23T00:42:23Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"We have to deal with two separate code paths: a normal rebase, which\nactually goes through git-am; and rebase {-m|-s}.\n\nThe only small issue with both is that they need to remember the\noriginal sha1 across a possible conflict resolution.  rebase -m\nalready puts this information in $dotest/current, and we just\nintroduce a similar file for git-am.\n\nNote that in git-am, the hook really only runs when coming from\ngit-rebase: the code path that sets the $dotest/original-commit file\nis guarded by a test for $dotest/rebasing.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\n\n git-am.sh                    |   10 ++++++++++\n git-rebase.sh                |    5 +++++\n t/t5407-post-rewrite-hook.sh |   30 ++++++++++++++++++++++++++++++\n 3 files changed, 45 insertions(+), 0 deletions(-)\n\ndiff --git a/git-am.sh b/git-am.sh\nindex b11af03..5a20a05 100755\n--- a/git-am.sh\n+++ b/git-am.sh\n@@ -575,6 +575,7 @@ do\n \t\t\techo \"Patch is empty.  Was it split wrong?\"\n \t\t\tstop_here $this\n \t\t}\n+\t\trm -f \"$dotest/original-commit\"\n \t\tif test -f \"$dotest/rebasing\" &&\n \t\t\tcommit=$(sed -e 's/^From \\([0-9a-f]*\\) .*/\\1/' \\\n \t\t\t\t-e q \"$dotest/$msgnum\") &&\n@@ -582,6 +583,7 @@ do\n \t\tthen\n \t\t\tgit cat-file commit \"$commit\" |\n \t\t\tsed -e '1,/^$/d' >\"$dotest/msg-clean\"\n+\t\t\techo \"$commit\" > \"$dotest/original-commit\"\n \t\telse\n \t\t\t{\n \t\t\t\tsed -n '/^Subject/ s/Subject: //p' \"$dotest/info\"\n@@ -765,6 +767,10 @@ do\n \tgit update-ref -m \"$GIT_REFLOG_ACTION: $FIRSTLINE\" HEAD $commit $parent ||\n \tstop_here $this\n \n+\tif test -f \"$dotest/original-commit\"; then\n+\t\techo \"$(cat \"$dotest/original-commit\") $commit\" >> \"$dotest/rewritten\"\n+\tfi\n+\n \tif test -x \"$GIT_DIR\"/hooks/post-applypatch\n \tthen\n \t\t\"$GIT_DIR\"/hooks/post-applypatch\n@@ -773,6 +779,10 @@ do\n \tgo_next\n done\n \n+if test -s \"$dotest\"/rewritten && test -x \"$GIT_DIR\"/hooks/post-rewrite; then\n+\t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$dotest\"/rewritten\n+fi\n+\n git gc --auto\n \n rm -fr \"$dotest\"\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex fb4fef7..52f8b9b 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -79,6 +79,7 @@ continue_merge () {\n \t\tthen\n \t\t\tprintf \"Committed: %0${prec}d \" $msgnum\n \t\tfi\n+\t\techo \"$cmt $(git rev-parse HEAD^0)\" >> \"$dotest/rewritten\"\n \telse\n \t\tif test -z \"$GIT_QUIET\"\n \t\tthen\n@@ -151,6 +152,10 @@ move_to_original_branch () {\n \n finish_rb_merge () {\n \tmove_to_original_branch\n+\tif test -x \"$GIT_DIR\"/hooks/post-rewrite &&\n+\t\ttest -s \"$dotest\"/rewritten; then\n+\t\t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$dotest\"/rewritten\n+\tfi\n \trm -r \"$dotest\"\n \tsay All done.\n }\ndiff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh\nindex 1020af9..1ecaa4b 100755\n--- a/t/t5407-post-rewrite-hook.sh\n+++ b/t/t5407-post-rewrite-hook.sh\n@@ -49,4 +49,34 @@ test_expect_success 'git commit --amend --no-post-rewrite' '\n \ttest ! -f post-rewrite.data\n '\n \n+test_expect_success 'git rebase' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\ttest_must_fail git rebase --onto A B &&\n+\techo C > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse C) $(git rev-parse HEAD^)\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git rebase --skip' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\ttest_must_fail git rebase --onto A B &&\n+\ttest_must_fail git rebase --skip &&\n+\techo D > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n test_done\n-- \n1.7.0.218.g73a398\n"},{"id":"135383","messageId":"87f20b987fa7a389e89f8931d553de7095d2bc86.1266885599.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266885599.git.trast@student.ethz.ch","subject":"[PATCH v5 06/11] rebase -i: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T00:42:24Z","receivedAt":"2010-02-23T00:42:24Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Aside from the same issue that rebase also has (remembering the\noriginal commit across a conflict resolution), rebase -i brings an\nextra twist: We need to defer writing the rewritten list in the case\nof {squash,fixup} because their rewritten result should be the last\ncommit in the squashed group.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\n\n git-rebase--interactive.sh   |   46 +++++++++++++++++++++-\n t/t5407-post-rewrite-hook.sh |   90 ++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 135 insertions(+), 1 deletions(-)\n\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex 5735859..564f6ac 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -96,6 +96,13 @@ AUTHOR_SCRIPT=\"$DOTEST\"/author-script\n # command is processed, this file is deleted.\n AMEND=\"$DOTEST\"/amend\n \n+# For the post-rewrite hook, we make a list of rewritten commits and\n+# their new sha1s.  The rewritten-pending list keeps the sha1s of\n+# commits that have been processed, but not committed yet,\n+# e.g. because they are waiting for a 'squash' command.\n+REWRITTEN_LIST=\"$DOTEST\"/rewritten-list\n+REWRITTEN_PENDING=\"$DOTEST\"/rewritten-pending\n+\n PRESERVE_MERGES=\n STRATEGY=\n ONTO=\n@@ -198,6 +205,7 @@ make_patch () {\n }\n \n die_with_patch () {\n+\techo \"$1\" > \"$DOTEST\"/stopped-sha\n \tmake_patch \"$1\"\n \tgit rerere\n \tdie \"$2\"\n@@ -348,6 +356,7 @@ pick_one_preserving_merges () {\n \t\t\t\tprintf \"%s\\n\" \"$msg\" > \"$GIT_DIR\"/MERGE_MSG\n \t\t\t\tdie_with_patch $sha1 \"Error redoing merge $sha1\"\n \t\t\tfi\n+\t\t\techo \"$sha1 $(git rev-parse HEAD^0)\" >> \"$REWRITTEN_LIST\"\n \t\t\t;;\n \t\t*)\n \t\t\toutput git cherry-pick \"$@\" ||\n@@ -425,6 +434,26 @@ die_failed_squash() {\n \tdie_with_patch $1 \"\"\n }\n \n+flush_rewritten_pending() {\n+\ttest -s \"$REWRITTEN_PENDING\" || return\n+\tnewsha1=\"$(git rev-parse HEAD^0)\"\n+\tsed \"s/$/ $newsha1/\" < \"$REWRITTEN_PENDING\" >> \"$REWRITTEN_LIST\"\n+\trm -f \"$REWRITTEN_PENDING\"\n+}\n+\n+record_in_rewritten() {\n+\toldsha1=\"$(git rev-parse $1)\"\n+\techo \"$oldsha1\" >> \"$REWRITTEN_PENDING\"\n+\n+\tcase \"$(peek_next_command)\" in\n+\t    squash|s|fixup|f)\n+\t\t;;\n+\t    *)\n+\t\tflush_rewritten_pending\n+\t\t;;\n+\tesac\n+}\n+\n do_next () {\n \trm -f \"$MSG\" \"$AUTHOR_SCRIPT\" \"$AMEND\" || exit\n \tread command sha1 rest < \"$TODO\"\n@@ -438,6 +467,7 @@ do_next () {\n \t\tmark_action_done\n \t\tpick_one $sha1 ||\n \t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n+\t\trecord_in_rewritten $sha1\n \t\t;;\n \treword|r)\n \t\tcomment_for_reflog reword\n@@ -446,6 +476,7 @@ do_next () {\n \t\tpick_one $sha1 ||\n \t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n \t\tgit commit --amend --no-post-rewrite\n+\t\trecord_in_rewritten $sha1\n \t\t;;\n \tedit|e)\n \t\tcomment_for_reflog edit\n@@ -453,6 +484,7 @@ do_next () {\n \t\tmark_action_done\n \t\tpick_one $sha1 ||\n \t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n+\t\techo \"$1\" > \"$DOTEST\"/stopped-sha\n \t\tmake_patch $sha1\n \t\tgit rev-parse --verify HEAD > \"$AMEND\"\n \t\twarn \"Stopped at $sha1... $rest\"\n@@ -509,6 +541,7 @@ do_next () {\n \t\t\trm -f \"$SQUASH_MSG\" \"$FIXUP_MSG\"\n \t\t\t;;\n \t\tesac\n+\t\trecord_in_rewritten $sha1\n \t\t;;\n \t*)\n \t\twarn \"Unknown command: $command $sha1 $rest\"\n@@ -537,6 +570,11 @@ do_next () {\n \t\ttest ! -f \"$DOTEST\"/verbose ||\n \t\t\tgit diff-tree --stat $(cat \"$DOTEST\"/head)..HEAD\n \t} &&\n+\tif test -x \"$GIT_DIR\"/hooks/post-rewrite &&\n+\t\ttest -s \"$REWRITTEN_LIST\"; then\n+\t\t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$REWRITTEN_LIST\"\n+\t\ttrue # we don't care if this hook failed\n+\tfi &&\n \trm -rf \"$DOTEST\" &&\n \tgit gc --auto &&\n \twarn \"Successfully rebased and updated $HEADNAME.\"\n@@ -571,7 +609,12 @@ skip_unnecessary_picks () {\n \t\tesac\n \t\techo \"$command${sha1:+ }$sha1${rest:+ }$rest\" >&$fd\n \tdone <\"$TODO\" >\"$TODO.new\" 3>>\"$DONE\" &&\n-\tmv -f \"$TODO\".new \"$TODO\" ||\n+\tmv -f \"$TODO\".new \"$TODO\" &&\n+\tcase \"$(peek_next_command)\" in\n+\tsquash|s|fixup|f)\n+\t\trecord_in_rewritten \"$ONTO\"\n+\t\t;;\n+\tesac ||\n \tdie \"Could not skip unnecessary pick commands\"\n }\n \n@@ -685,6 +728,7 @@ first and then run 'git rebase --continue' again.\"\n \t\t\t\ttest -n \"$amend\" && git reset --soft $amend\n \t\t\t\tdie \"Could not commit staged changes.\"\n \t\t\t}\n+\t\t\trecord_in_rewritten \"$(cat \"$DOTEST\"/stopped-sha)\"\n \t\tfi\n \n \t\trequire_clean_work_tree\ndiff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh\nindex 1ecaa4b..e1fccc4 100755\n--- a/t/t5407-post-rewrite-hook.sh\n+++ b/t/t5407-post-rewrite-hook.sh\n@@ -79,4 +79,94 @@ EOF\n \tverify_hook_input\n '\n \n+test_expect_success 'git rebase -m' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\ttest_must_fail git rebase -m --onto A B &&\n+\techo C > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse C) $(git rev-parse HEAD^)\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git rebase -m --skip' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\ttest_must_fail git rebase --onto A B &&\n+\ttest_must_fail git rebase --skip &&\n+\techo D > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+. \"$TEST_DIRECTORY\"/lib-rebase.sh\n+\n+set_fake_editor\n+\n+test_expect_success 'git rebase -i (unchanged)' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\tFAKE_LINES=\"1 2\" test_must_fail git rebase -i --onto A B &&\n+\techo C > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse C) $(git rev-parse HEAD^)\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git rebase -i (skip)' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\tFAKE_LINES=\"2\" test_must_fail git rebase -i --onto A B &&\n+\techo D > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git rebase -i (squash)' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\tFAKE_LINES=\"1 squash 2\" test_must_fail git rebase -i --onto A B &&\n+\techo C > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse C) $(git rev-parse HEAD)\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git rebase -i (fixup without conflict)' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\tFAKE_LINES=\"1 fixup 2\" git rebase -i B &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse C) $(git rev-parse HEAD)\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n test_done\n-- \n1.7.0.218.g73a398\n"},{"id":"135378","messageId":"ca7ae014d3e7f59e627bc72fbdda8b8f89e7469f.1266885599.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266885599.git.trast@student.ethz.ch","subject":"[PATCH v5 07/11] notes: implement 'git notes copy --stdin'","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T00:42:25Z","receivedAt":"2010-02-23T00:42:25Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"This implements a mass-copy command that takes a sequence of lines in\nthe format\n\n  <from-sha1> SP <to-sha1> [ SP <rest> ] LF\n\non stdin, and copies each <from-sha1>'s notes to the <to-sha1>.  The\n<rest> is ignored.  The intent, of course, is that this can read the\nsame input that the 'post-rewrite' hook gets.\n\nThe copy_note() function is exposed for everyone's and in particular\nthe next commit's use.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\n\n Documentation/git-notes.txt |   12 ++++++++-\n builtin-notes.c             |   56 ++++++++++++++++++++++++++++++++++++++++++-\n notes.c                     |   20 +++++++++++++++\n notes.h                     |    9 +++++++\n t/t3301-notes.sh            |   34 ++++++++++++++++++++++++++\n 5 files changed, 129 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex 14f73b9..f67cb6a 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n [verse]\n 'git notes' [list [<object>]]\n 'git notes' add [-f] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]\n-'git notes' copy [-f] <from-object> <to-object>\n+'git notes' copy [-f] ( --stdin | <from-object> <to-object> )\n 'git notes' append [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]\n 'git notes' edit [<object>]\n 'git notes' show [<object>]\n@@ -55,6 +55,16 @@ copy::\n \tobjects has none. (use -f to overwrite existing notes to the\n \tsecond object). This subcommand is equivalent to:\n \t`git notes add [-f] -C $(git notes list <from-object>) <to-object>`\n++\n+In `\\--stdin` mode, take lines in the format\n++\n+----------\n+<from-object> SP <to-object> [ SP <rest> ] LF\n+----------\n++\n+on standard input, and copy the notes from each <from-object> to its\n+corresponding <to-object>.  (The optional `<rest>` is ignored so that\n+the command can read the input given to the `post-rewrite` hook.)\n \n append::\n \tAppend to the notes of an existing object (defaults to HEAD).\ndiff --git a/builtin-notes.c b/builtin-notes.c\nindex 123ecad..daeb14e 100644\n--- a/builtin-notes.c\n+++ b/builtin-notes.c\n@@ -278,6 +278,46 @@ int commit_notes(struct notes_tree *t, const char *msg)\n \treturn 0;\n }\n \n+int notes_copy_from_stdin(int force)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct notes_tree *t;\n+\tint ret = 0;\n+\n+\tinit_notes(NULL, NULL, NULL, 0);\n+\tt = &default_notes_tree;\n+\n+\twhile (strbuf_getline(&buf, stdin, '\\n') != EOF) {\n+\t\tunsigned char from_obj[20], to_obj[20];\n+\t\tstruct strbuf **split;\n+\t\tint err;\n+\n+\t\tsplit = strbuf_split(&buf, ' ');\n+\t\tif (!split[0] || !split[1])\n+\t\t\tdie(\"Malformed input line: '%s'.\", buf.buf);\n+\t\tstrbuf_rtrim(split[0]);\n+\t\tstrbuf_rtrim(split[1]);\n+\t\tif (get_sha1(split[0]->buf, from_obj))\n+\t\t\tdie(\"Failed to resolve '%s' as a valid ref.\", split[0]->buf);\n+\t\tif (get_sha1(split[1]->buf, to_obj))\n+\t\t\tdie(\"Failed to resolve '%s' as a valid ref.\", split[1]->buf);\n+\n+\t\terr = copy_note(t, from_obj, to_obj, force, combine_notes_overwrite);\n+\n+\t\tif (err) {\n+\t\t\terror(\"Failed to copy notes from '%s' to '%s'\",\n+\t\t\t      split[0]->buf, split[1]->buf);\n+\t\t\tret = 1;\n+\t\t}\n+\n+\t\tstrbuf_list_free(split);\n+\t}\n+\n+\tcommit_notes(t, \"Notes added by 'git notes copy'\");\n+\tfree_notes(t);\n+\treturn ret;\n+}\n+\n int cmd_notes(int argc, const char **argv, const char *prefix)\n {\n \tstruct notes_tree *t;\n@@ -287,7 +327,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \tchar logmsg[100];\n \n \tint list = 0, add = 0, copy = 0, append = 0, edit = 0, show = 0,\n-\t    remove = 0, prune = 0, force = 0;\n+\t    remove = 0, prune = 0, force = 0, from_stdin = 0;\n \tint given_object = 0, i = 1, retval = 0;\n \tstruct msg_arg msg = { 0, 0, STRBUF_INIT };\n \tstruct option options[] = {\n@@ -301,6 +341,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\tOPT_CALLBACK('C', \"reuse-message\", &msg, \"OBJECT\",\n \t\t\t   \"reuse specified note object\", parse_reuse_arg),\n \t\tOPT_BOOLEAN('f', \"force\", &force, \"replace existing notes\"),\n+\t\tOPT_BOOLEAN(0, \"stdin\", &from_stdin, \"read objects from stdin\"),\n \t\tOPT_END()\n \t};\n \n@@ -349,8 +390,21 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(git_notes_usage, options);\n \t}\n \n+\tif (!copy && from_stdin) {\n+\t\terror(\"cannot use --stdin with %s subcommand.\", argv[0]);\n+\t\tusage_with_options(git_notes_usage, options);\n+\t}\n+\n \tif (copy) {\n \t\tconst char *from_ref;\n+\t\tif (from_stdin) {\n+\t\t\tif (argc > 1) {\n+\t\t\t\terror(\"too many parameters\");\n+\t\t\t\tusage_with_options(git_notes_usage, options);\n+\t\t\t} else {\n+\t\t\t\treturn notes_copy_from_stdin(force);\n+\t\t\t}\n+\t\t}\n \t\tif (argc < 3) {\n \t\t\terror(\"too few parameters\");\n \t\t\tusage_with_options(git_notes_usage, options);\ndiff --git a/notes.c b/notes.c\nindex ee54a42..9246358 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -1187,3 +1187,23 @@ void format_display_notes(const unsigned char *object_sha1,\n \t\tformat_note(display_notes_trees[i], object_sha1, sb,\n \t\t\t    output_encoding, flags);\n }\n+\n+int copy_note(struct notes_tree *t,\n+\t      const unsigned char *from_obj, const unsigned char *to_obj,\n+\t      int force, combine_notes_fn combine_fn)\n+{\n+\tconst unsigned char *note = get_note(t, from_obj);\n+\tconst unsigned char *existing_note = get_note(t, to_obj);\n+\n+\tif (!force && existing_note)\n+\t\treturn 1;\n+\n+\tif (note)\n+\t\tadd_note(t, to_obj, note, combine_fn);\n+\telse if (existing_note) {\n+\t\tadd_note(t, to_obj, null_sha1, combine_fn);\n+\t} else\n+\t\tremove_note(t, to_obj);\n+\n+\treturn 0;\n+}\ndiff --git a/notes.h b/notes.h\nindex 7650254..ec613ca 100644\n--- a/notes.h\n+++ b/notes.h\n@@ -100,6 +100,15 @@ void add_note(struct notes_tree *t, const unsigned char *object_sha1,\n \t\tconst unsigned char *object_sha1);\n \n /*\n+ * Copy a note from one object to another in the given notes_tree.\n+ *\n+ * Fails if the to_obj already has a note unless 'force' is true.\n+ */\n+int copy_note(struct notes_tree *t,\n+\t      const unsigned char *from_obj, const unsigned char *to_obj,\n+\t      int force, combine_notes_fn combine_fn);\n+\n+/*\n  * Flags controlling behaviour of for_each_note()\n  *\n  * Default behaviour of for_each_note() is to traverse every single note object\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex cb7166f..60ad6a1 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -776,4 +776,38 @@ test_expect_success 'cannot copy note from object without notes' '\n \ttest_must_fail git notes copy HEAD^ HEAD\n '\n \n+cat > expect << EOF\n+commit e5d4fb5698d564ab8c73551538ecaf2b0c666185\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:25:13 2005 -0700\n+\n+    13th\n+\n+Notes (other):\n+    yet another note\n+$whitespace\n+    yet another note\n+\n+commit 7038787dfe22a14c3867ce816dbba39845359719\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:24:13 2005 -0700\n+\n+    12th\n+\n+Notes (other):\n+    other note\n+$whitespace\n+    yet another note\n+EOF\n+\n+test_expect_success 'git notes copy --stdin' '\n+\t(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \\\n+\techo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |\n+\tgit notes copy --stdin &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect output &&\n+\ttest \"$(git notes list HEAD)\" = \"$(git notes list HEAD~2)\" &&\n+\ttest \"$(git notes list HEAD^)\" = \"$(git notes list HEAD~3)\"\n+'\n+\n test_done\n-- \n1.7.0.218.g73a398\n"},{"id":"135379","messageId":"52e4eb4a01db6a31c3e5395227ee1f78af835139.1266885599.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266885599.git.trast@student.ethz.ch","subject":"[PATCH v5 08/11] notes: implement helpers needed for note copying during rewrite","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T00:42:26Z","receivedAt":"2010-02-23T00:42:26Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Implement helper functions to load the rewriting config, and to\nactually copy the notes.  Also document the config.\n\nSecondly, also implement an undocumented --for-rewrite=<cmd> option to\n'git notes copy' which is used like --stdin, but also puts the\nconfiguration for <cmd> into effect.  It will be needed to support the\ncopying in git-rebase.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nChanges since v4:\n* doc tweaks\n* enforce full qualification of refs\n\n Documentation/config.txt    |   30 +++++++\n Documentation/git-notes.txt |    4 +\n Documentation/githooks.txt  |    4 +\n builtin-notes.c             |  139 +++++++++++++++++++++++++++++--\n builtin.h                   |   18 ++++\n cache.h                     |    2 +\n t/t3301-notes.sh            |  195 +++++++++++++++++++++++++++++++++++++++++++\n t/test-lib.sh               |    2 +\n 8 files changed, 386 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex e077445..dffe1f6 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1324,6 +1324,36 @@ The effective value of \"core.notesRef\" (possibly overridden by\n GIT_NOTES_REF) is also implicitly added to the list of refs to be\n displayed.\n \n+notes.rewrite.<command>::\n+\tWhen rewriting commits with <command> (currently `amend` or\n+\t`rebase`) and this variable is set to `true`, git\n+\tautomatically copies your notes from the original to the\n+\trewritten commit.  Defaults to `true`, but see\n+\t\"notes.rewriteRef\" below.\n++\n+This setting can be overridden with the `GIT_NOTES_REWRITE_REF`\n+environment variable, which must be a colon separated list of refs or\n+globs.\n+\n+notes.rewriteMode::\n+\tWhen copying notes during a rewrite (see the\n+\t\"notes.rewrite.<command>\" option), determines what to do if\n+\tthe target commit already has a note.  Must be one of\n+\t`overwrite`, `concatenate`, or `ignore`.  Defaults to\n+\t`concatenate`.\n++\n+This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`\n+environment variable.\n+\n+notes.rewriteRef::\n+\tWhen copying notes during a rewrite, specifies the (fully\n+\tqualified) ref whose notes should be copied.  The ref may be a\n+\tglob, in which case notes in all matching refs will be copied.\n+\tYou may also specify this configuration several times.\n++\n+Does not have a default value; you must configure this variable to\n+enable note rewriting.\n+\n pack.window::\n \tThe size of the window used by linkgit:git-pack-objects[1] when no\n \twindow size is given on the command line. Defaults to 10.\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex f67cb6a..92f1249 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -34,6 +34,10 @@ the empty string.  Alternatively, you can set it to a different ref,\n something like \"refs/notes/bugzilla\".  This setting can be overridden\n by the environment variable \"GIT_NOTES_REF\".\n \n+See the description of \"notes.rewrite.<command>\" in\n+linkgit:git-config[1] for a way of carrying your notes across commands\n+that rewrite commits.\n+\n \n SUBCOMMANDS\n -----------\ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex a741769..7183aa9 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -335,6 +335,10 @@ The 'extra-info' is again command-dependent.  If it is empty, the\n preceding SP is also omitted.  Currently, no commands pass any\n 'extra-info'.\n \n+The hook always runs after the automatic note copying (see\n+\"notes.rewrite.<command>\" in linkgit:git-config.txt) has happened, and\n+thus has access to these notes.\n+\n The following command-specific comments apply:\n \n rebase::\ndiff --git a/builtin-notes.c b/builtin-notes.c\nindex daeb14e..026cfd3 100644\n--- a/builtin-notes.c\n+++ b/builtin-notes.c\n@@ -16,6 +16,7 @@\n #include \"exec_cmd.h\"\n #include \"run-command.h\"\n #include \"parse-options.h\"\n+#include \"string-list.h\"\n \n static const char * const git_notes_usage[] = {\n \t\"git notes [list [<object>]]\",\n@@ -278,14 +279,121 @@ int commit_notes(struct notes_tree *t, const char *msg)\n \treturn 0;\n }\n \n-int notes_copy_from_stdin(int force)\n+\n+combine_notes_fn *parse_combine_notes_fn(const char *v)\n+{\n+\tif (!strcasecmp(v, \"overwrite\"))\n+\t\treturn combine_notes_overwrite;\n+\telse if (!strcasecmp(v, \"ignore\"))\n+\t\treturn combine_notes_ignore;\n+\telse if (!strcasecmp(v, \"concatenate\"))\n+\t\treturn combine_notes_concatenate;\n+\telse\n+\t\treturn NULL;\n+}\n+\n+static int notes_rewrite_config(const char *k, const char *v, void *cb)\n+{\n+\tstruct notes_rewrite_cfg *c = cb;\n+\tif (!prefixcmp(k, \"notes.rewrite.\") && !strcmp(k+14, c->cmd)) {\n+\t\tc->enabled = git_config_bool(k, v);\n+\t\treturn 0;\n+\t} else if (!c->mode_from_env && !strcmp(k, \"notes.rewritemode\")) {\n+\t\tif (!v)\n+\t\t\tconfig_error_nonbool(k);\n+\t\tc->combine = parse_combine_notes_fn(v);\n+\t\tif (!c->combine) {\n+\t\t\terror(\"Bad notes.rewriteMode value: '%s'\", v);\n+\t\t\treturn 1;\n+\t\t}\n+\t\treturn 0;\n+\t} else if (!c->refs_from_env && !strcmp(k, \"notes.rewriteref\")) {\n+\t\t/* note that a refs/ prefix is implied in the\n+\t\t * underlying for_each_glob_ref */\n+\t\tif (!prefixcmp(v, \"refs/notes/\"))\n+\t\t\tstring_list_add_refs_by_glob(c->refs, v);\n+\t\telse\n+\t\t\twarning(\"Refusing to rewrite notes in %s\"\n+\t\t\t\t\" (outside of refs/notes/)\", v);\n+\t\treturn 0;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+\n+struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)\n+{\n+\tstruct notes_rewrite_cfg *c = xmalloc(sizeof(struct notes_rewrite_cfg));\n+\tconst char *rewrite_mode_env = getenv(GIT_NOTES_REWRITE_MODE_ENVIRONMENT);\n+\tconst char *rewrite_refs_env = getenv(GIT_NOTES_REWRITE_REF_ENVIRONMENT);\n+\tc->cmd = cmd;\n+\tc->enabled = 1;\n+\tc->combine = combine_notes_concatenate;\n+\tc->refs = xcalloc(1, sizeof(struct string_list));\n+\tc->refs->strdup_strings = 1;\n+\tc->refs_from_env = 0;\n+\tc->mode_from_env = 0;\n+\tif (rewrite_mode_env) {\n+\t\tc->mode_from_env = 1;\n+\t\tc->combine = parse_combine_notes_fn(rewrite_mode_env);\n+\t\tif (!c->combine)\n+\t\t\terror(\"Bad \" GIT_NOTES_REWRITE_MODE_ENVIRONMENT\n+\t\t\t      \" value: '%s'\", rewrite_mode_env);\n+\t}\n+\tif (rewrite_refs_env) {\n+\t\tc->refs_from_env = 1;\n+\t\tstring_list_add_refs_from_colon_sep(c->refs, rewrite_refs_env);\n+\t}\n+\tgit_config(notes_rewrite_config, c);\n+\tif (!c->enabled || !c->refs->nr) {\n+\t\tstring_list_clear(c->refs, 0);\n+\t\tfree(c->refs);\n+\t\tfree(c);\n+\t\treturn NULL;\n+\t}\n+\tc->trees = load_notes_trees(c->refs);\n+\tstring_list_clear(c->refs, 0);\n+\tfree(c->refs);\n+\treturn c;\n+}\n+\n+int copy_note_for_rewrite(struct notes_rewrite_cfg *c,\n+\t\t\t  const unsigned char *from_obj, const unsigned char *to_obj)\n+{\n+\tint ret = 0;\n+\tint i;\n+\tfor (i = 0; c->trees[i]; i++)\n+\t\tret = copy_note(c->trees[i], from_obj, to_obj, 1, c->combine) || ret;\n+\treturn ret;\n+}\n+\n+void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c)\n+{\n+\tint i;\n+\tfor (i = 0; c->trees[i]; i++) {\n+\t\tcommit_notes(c->trees[i], \"Notes added by 'git notes copy'\");\n+\t\tfree_notes(c->trees[i]);\n+\t}\n+\tfree(c->trees);\n+\tfree(c);\n+}\n+\n+int notes_copy_from_stdin(int force, const char *rewrite_cmd)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n+\tstruct notes_rewrite_cfg *c = NULL;\n \tstruct notes_tree *t;\n \tint ret = 0;\n \n-\tinit_notes(NULL, NULL, NULL, 0);\n-\tt = &default_notes_tree;\n+\tif (rewrite_cmd) {\n+\t\tc = init_copy_notes_for_rewrite(rewrite_cmd);\n+\t\tif (!c)\n+\t\t\treturn 0;\n+\t} else {\n+\t\tinit_notes(NULL, NULL, NULL, 0);\n+\t\tt = &default_notes_tree;\n+\t}\n \n \twhile (strbuf_getline(&buf, stdin, '\\n') != EOF) {\n \t\tunsigned char from_obj[20], to_obj[20];\n@@ -302,7 +410,11 @@ int notes_copy_from_stdin(int force)\n \t\tif (get_sha1(split[1]->buf, to_obj))\n \t\t\tdie(\"Failed to resolve '%s' as a valid ref.\", split[1]->buf);\n \n-\t\terr = copy_note(t, from_obj, to_obj, force, combine_notes_overwrite);\n+\t\tif (rewrite_cmd)\n+\t\t\terr = copy_note_for_rewrite(c, from_obj, to_obj);\n+\t\telse\n+\t\t\terr = copy_note(t, from_obj, to_obj, force,\n+\t\t\t\t\tcombine_notes_overwrite);\n \n \t\tif (err) {\n \t\t\terror(\"Failed to copy notes from '%s' to '%s'\",\n@@ -313,8 +425,12 @@ int notes_copy_from_stdin(int force)\n \t\tstrbuf_list_free(split);\n \t}\n \n-\tcommit_notes(t, \"Notes added by 'git notes copy'\");\n-\tfree_notes(t);\n+\tif (!rewrite_cmd) {\n+\t\tcommit_notes(t, \"Notes added by 'git notes copy'\");\n+\t\tfree_notes(t);\n+\t} else {\n+\t\tfinish_copy_notes_for_rewrite(c);\n+\t}\n \treturn ret;\n }\n \n@@ -330,6 +446,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t    remove = 0, prune = 0, force = 0, from_stdin = 0;\n \tint given_object = 0, i = 1, retval = 0;\n \tstruct msg_arg msg = { 0, 0, STRBUF_INIT };\n+\tconst char *rewrite_cmd = NULL;\n \tstruct option options[] = {\n \t\tOPT_GROUP(\"Notes options\"),\n \t\tOPT_CALLBACK('m', \"message\", &msg, \"MSG\",\n@@ -342,6 +459,8 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\t\t   \"reuse specified note object\", parse_reuse_arg),\n \t\tOPT_BOOLEAN('f', \"force\", &force, \"replace existing notes\"),\n \t\tOPT_BOOLEAN(0, \"stdin\", &from_stdin, \"read objects from stdin\"),\n+\t\tOPT_STRING(0, \"for-rewrite\", &rewrite_cmd, \"command\",\n+\t\t\t   \"load rewriting config for <command> (implies --stdin)\"),\n \t\tOPT_END()\n \t};\n \n@@ -390,6 +509,10 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(git_notes_usage, options);\n \t}\n \n+\tif (!copy && rewrite_cmd) {\n+\t\terror(\"cannot use --for-rewrite with %s subcommand.\", argv[0]);\n+\t\tusage_with_options(git_notes_usage, options);\n+\t}\n \tif (!copy && from_stdin) {\n \t\terror(\"cannot use --stdin with %s subcommand.\", argv[0]);\n \t\tusage_with_options(git_notes_usage, options);\n@@ -397,12 +520,12 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \n \tif (copy) {\n \t\tconst char *from_ref;\n-\t\tif (from_stdin) {\n+\t\tif (from_stdin || rewrite_cmd) {\n \t\t\tif (argc > 1) {\n \t\t\t\terror(\"too many parameters\");\n \t\t\t\tusage_with_options(git_notes_usage, options);\n \t\t\t} else {\n-\t\t\t\treturn notes_copy_from_stdin(force);\n+\t\t\t\treturn notes_copy_from_stdin(force, rewrite_cmd);\n \t\t\t}\n \t\t}\n \t\tif (argc < 3) {\ndiff --git a/builtin.h b/builtin.h\nindex cdf9847..8aebe61 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -20,6 +20,24 @@ extern int commit_tree(const char *msg, unsigned char *tree,\n \t\tstruct commit_list *parents, unsigned char *ret,\n \t\tconst char *author);\n extern int commit_notes(struct notes_tree *t, const char *msg);\n+\n+struct notes_rewrite_cfg\n+{\n+\tstruct notes_tree **trees;\n+\tconst char *cmd;\n+\tint enabled;\n+\tcombine_notes_fn *combine;\n+\tstruct string_list *refs;\n+\tint refs_from_env;\n+\tint mode_from_env;\n+};\n+\n+combine_notes_fn *parse_combine_notes_fn(const char *v);\n+struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd);\n+int copy_note_for_rewrite(struct notes_rewrite_cfg *c,\n+\t\t\t  const unsigned char *from_obj, const unsigned char *to_obj);\n+void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);\n+\n extern int check_pager_config(const char *cmd);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\ndiff --git a/cache.h b/cache.h\nindex ee4ae3e..39450bc 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -388,6 +388,8 @@ static inline enum object_type object_type(unsigned int mode)\n #define GIT_NOTES_REF_ENVIRONMENT \"GIT_NOTES_REF\"\n #define GIT_NOTES_DEFAULT_REF \"refs/notes/commits\"\n #define GIT_NOTES_DISPLAY_REF_ENVIRONMENT \"GIT_NOTES_DISPLAY_REF\"\n+#define GIT_NOTES_REWRITE_REF_ENVIRONMENT \"GIT_NOTES_REWRITE_REF\"\n+#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT \"GIT_NOTES_REWRITE_MODE\"\n \n extern int is_bare_repository_cfg;\n extern int is_bare_repository(void);\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex 60ad6a1..aeec90a 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -810,4 +810,199 @@ test_expect_success 'git notes copy --stdin' '\n \ttest \"$(git notes list HEAD^)\" = \"$(git notes list HEAD~3)\"\n '\n \n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:26:13 2005 -0700\n+\n+    14th\n+EOF\n+\n+test_expect_success 'git notes copy --for-rewrite (unconfigured)' '\n+\ttest_commit 14th &&\n+\ttest_commit 15th &&\n+\t(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \\\n+\techo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes (other):\n+    yet another note\n+$whitespace\n+    yet another note\n+\n+commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:26:13 2005 -0700\n+\n+    14th\n+\n+Notes (other):\n+    other note\n+$whitespace\n+    yet another note\n+EOF\n+\n+test_expect_success 'git notes copy --for-rewrite (enabled)' '\n+\tgit config notes.rewriteMode overwrite &&\n+\tgit config notes.rewriteRef \"refs/notes/*\" &&\n+\t(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \\\n+\techo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'git notes copy --for-rewrite (disabled)' '\n+\tgit config notes.rewrite.bar false &&\n+\techo $(git rev-parse HEAD~3) $(git rev-parse HEAD) |\n+\tgit notes copy --for-rewrite=bar &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes (other):\n+    a fresh note\n+EOF\n+\n+test_expect_success 'git notes copy --for-rewrite (overwrite)' '\n+\tgit notes add -f -m\"a fresh note\" HEAD^ &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'git notes copy --for-rewrite (ignore)' '\n+\tgit config notes.rewriteMode ignore &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes (other):\n+    a fresh note\n+    another fresh note\n+EOF\n+\n+test_expect_success 'git notes copy --for-rewrite (append)' '\n+\tgit notes add -f -m\"another fresh note\" HEAD^ &&\n+\tgit config notes.rewriteMode concatenate &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes (other):\n+    a fresh note\n+    another fresh note\n+    append 1\n+    append 2\n+EOF\n+\n+test_expect_success 'git notes copy --for-rewrite (append two to one)' '\n+\tgit notes add -f -m\"append 1\" HEAD^ &&\n+\tgit notes add -f -m\"append 2\" HEAD^^ &&\n+\t(echo $(git rev-parse HEAD^) $(git rev-parse HEAD);\n+\techo $(git rev-parse HEAD^^) $(git rev-parse HEAD)) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'git notes copy --for-rewrite (append empty)' '\n+\tgit notes remove HEAD^ &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes (other):\n+    replacement note 1\n+EOF\n+\n+test_expect_success 'GIT_NOTES_REWRITE_MODE works' '\n+\tgit notes add -f -m\"replacement note 1\" HEAD^ &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tGIT_NOTES_REWRITE_MODE=overwrite git notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes (other):\n+    replacement note 2\n+EOF\n+\n+test_expect_success 'GIT_NOTES_REWRITE_REF works' '\n+\tgit config notes.rewriteMode overwrite &&\n+\tgit notes add -f -m\"replacement note 2\" HEAD^ &&\n+\tgit config --unset-all notes.rewriteRef &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tGIT_NOTES_REWRITE_REF=refs/notes/commits:refs/notes/other \\\n+\t\tgit notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'GIT_NOTES_REWRITE_REF overrides config' '\n+\tgit config notes.rewriteRef refs/notes/other &&\n+\tgit notes add -f -m\"replacement note 3\" HEAD^ &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tGIT_NOTES_REWRITE_REF= git notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n test_done\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex c814e9c..4caad9f 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -56,6 +56,8 @@ unset SHA1_FILE_DIRECTORIES\n unset SHA1_FILE_DIRECTORY\n unset GIT_NOTES_REF\n unset GIT_NOTES_DISPLAY_REF\n+unset GIT_NOTES_REWRITE_REF\n+unset GIT_NOTES_REWRITE_MODE\n GIT_MERGE_VERBOSITY=5\n export GIT_MERGE_VERBOSITY\n export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME\n-- \n1.7.0.218.g73a398\n"},{"id":"135381","messageId":"4623296da9ed82447718b46c72e6a22ff0152fc0.1266885599.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266885599.git.trast@student.ethz.ch","subject":"[PATCH v5 09/11] rebase: support automatic notes copying","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T00:42:27Z","receivedAt":"2010-02-23T00:42:27Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Luckily, all the support already happens to be there.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\n\n git-am.sh                     |    5 ++++-\n git-rebase--interactive.sh    |    4 ++++\n git-rebase.sh                 |    1 +\n t/t3400-rebase.sh             |   17 +++++++++++++++++\n t/t3404-rebase-interactive.sh |   24 ++++++++++++++++++++++++\n 5 files changed, 50 insertions(+), 1 deletions(-)\n\ndiff --git a/git-am.sh b/git-am.sh\nindex 5a20a05..a0ed1a6 100755\n--- a/git-am.sh\n+++ b/git-am.sh\n@@ -779,8 +779,11 @@ do\n \tgo_next\n done\n \n-if test -s \"$dotest\"/rewritten && test -x \"$GIT_DIR\"/hooks/post-rewrite; then\n+if test -s \"$dotest\"/rewritten; then\n+    git notes copy --for-rewrite=rebase < \"$dotest\"/rewritten\n+    if test -x \"$GIT_DIR\"/hooks/post-rewrite; then\n \t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$dotest\"/rewritten\n+    fi\n fi\n \n git gc --auto\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex 564f6ac..415ae72 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -570,6 +570,10 @@ do_next () {\n \t\ttest ! -f \"$DOTEST\"/verbose ||\n \t\t\tgit diff-tree --stat $(cat \"$DOTEST\"/head)..HEAD\n \t} &&\n+\t{\n+\t\tgit notes copy --for-rewrite=rebase < \"$REWRITTEN_LIST\" ||\n+\t\ttrue # we don't care if this copying failed\n+\t} &&\n \tif test -x \"$GIT_DIR\"/hooks/post-rewrite &&\n \t\ttest -s \"$REWRITTEN_LIST\"; then\n \t\t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$REWRITTEN_LIST\"\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex 52f8b9b..e0eb956 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -152,6 +152,7 @@ move_to_original_branch () {\n \n finish_rb_merge () {\n \tmove_to_original_branch\n+\tgit notes copy --for-rewrite=rebase < \"$dotest\"/rewritten\n \tif test -x \"$GIT_DIR\"/hooks/post-rewrite &&\n \t\ttest -s \"$dotest\"/rewritten; then\n \t\t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$dotest\"/rewritten\ndiff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh\nindex 4314ad2..dbf7dfb 100755\n--- a/t/t3400-rebase.sh\n+++ b/t/t3400-rebase.sh\n@@ -151,4 +151,21 @@ test_expect_success 'Rebase a commit that sprinkles CRs in' '\n \tgit diff --exit-code file-with-cr:CR HEAD:CR\n '\n \n+test_expect_success 'rebase can copy notes' '\n+\tgit config notes.rewrite.rebase true &&\n+\tgit config notes.rewriteRef \"refs/notes/*\" &&\n+\ttest_commit n1 &&\n+\ttest_commit n2 &&\n+\ttest_commit n3 &&\n+\tgit notes add -m\"a note\" n3 &&\n+\tgit rebase --onto n1 n2 &&\n+\ttest \"a note\" = \"$(git notes show HEAD)\"\n+'\n+\n+test_expect_success 'rebase -m can copy notes' '\n+\tgit reset --hard n3 &&\n+\tgit rebase -m --onto n1 n2 &&\n+\ttest \"a note\" = \"$(git notes show HEAD)\"\n+'\n+\n test_done\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 4e35137..19668c2 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -553,4 +553,28 @@ test_expect_success 'reword' '\n \tgit show HEAD~2 | grep \"C changed\"\n '\n \n+test_expect_success 'rebase -i can copy notes' '\n+\tgit config notes.rewrite.rebase true &&\n+\tgit config notes.rewriteRef \"refs/notes/*\" &&\n+\ttest_commit n1 &&\n+\ttest_commit n2 &&\n+\ttest_commit n3 &&\n+\tgit notes add -m\"a note\" n3 &&\n+\tgit rebase --onto n1 n2 &&\n+\ttest \"a note\" = \"$(git notes show HEAD)\"\n+'\n+\n+cat >expect <<EOF\n+an earlier note\n+a note\n+EOF\n+\n+test_expect_success 'rebase -i can copy notes over a fixup' '\n+\tgit reset --hard n3 &&\n+\tgit notes add -m\"an earlier note\" n2 &&\n+\tGIT_NOTES_REWRITE_MODE=concatenate FAKE_LINES=\"1 fixup 2\" git rebase -i n1 &&\n+\tgit notes show > output &&\n+\ttest_cmp expect output\n+'\n+\n test_done\n-- \n1.7.0.218.g73a398\n"},{"id":"135377","messageId":"c5db2ed2e3215b3e84cc335301eddcbb21dfd5c7.1266885599.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266885599.git.trast@student.ethz.ch","subject":"[PATCH v5 10/11] commit --amend: copy notes to the new commit","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T00:42:28Z","receivedAt":"2010-02-23T00:42:28Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Teaches 'git commit --amend' to copy notes.  The catch is that this\nmust also be guarded by --no-post-rewrite, which we use to prevent\n--amend from copying notes during a rebase -i 'edit'/'reword'.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\n\n builtin-commit.c  |    6 ++++++\n t/t7501-commit.sh |   12 ++++++++++++\n 2 files changed, 18 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 1c3aa7d..3f1708b 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -1340,6 +1340,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \trerere(0);\n \trun_hook(get_index_file(), \"post-commit\", NULL);\n \tif (amend && !no_post_rewrite) {\n+\t\tstruct notes_rewrite_cfg *cfg;\n+\t\tcfg = init_copy_notes_for_rewrite(\"amend\");\n+\t\tif (cfg) {\n+\t\t\tcopy_note_for_rewrite(cfg, head_sha1, commit_sha1);\n+\t\t\tfinish_copy_notes_for_rewrite(cfg);\n+\t\t}\n \t\trun_rewrite_hook(head_sha1, commit_sha1);\n \t}\n \tif (!quiet)\ndiff --git a/t/t7501-commit.sh b/t/t7501-commit.sh\nindex 7940901..8297cb4 100755\n--- a/t/t7501-commit.sh\n+++ b/t/t7501-commit.sh\n@@ -425,4 +425,16 @@ test_expect_success 'amend using the message from a commit named with tag' '\n \n '\n \n+test_expect_success 'amend can copy notes' '\n+\n+\tgit config notes.rewrite.amend true &&\n+\tgit config notes.rewriteRef \"refs/notes/*\" &&\n+\ttest_commit foo &&\n+\tgit notes add -m\"a note\" &&\n+\ttest_tick &&\n+\tgit commit --amend -m\"new foo\" &&\n+\ttest \"$(git notes show)\" = \"a note\"\n+\n+'\n+\n test_done\n-- \n1.7.0.218.g73a398\n"},{"id":"135382","messageId":"cb91ce31f300d774d230eaf7f57c37955caca673.1266885599.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266885599.git.trast@student.ethz.ch","subject":"[PATCH v5 11/11] notes: add shorthand --ref to override GIT_NOTES_REF","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T00:42:29Z","receivedAt":"2010-02-23T00:42:29Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Adds a shorthand option that overrides the GIT_NOTES_REF variable, and\nhence determines the notes tree that will be manipulated.  It also\nDWIMs a refs/notes/ prefix.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nChanges since v4:\n* doc tweaks\n\n Documentation/git-notes.txt |    5 +++++\n builtin-notes.c             |   16 ++++++++++++++++\n 2 files changed, 21 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex 92f1249..467a963 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -115,6 +115,11 @@ OPTIONS\n \tLike '-C', but with '-c' the editor is invoked, so that\n \tthe user can further edit the note message.\n \n+--ref <ref>::\n+\tManipulate the notes tree in <ref>.  This overrides both\n+\tGIT_NOTES_REF and the \"core.notesRef\" configuration.  The ref\n+\tis taken to be in `refs/notes/` if it is not qualified.\n+\n Author\n ------\n Written by Johannes Schindelin <johannes.schindelin@gmx.de> and\ndiff --git a/builtin-notes.c b/builtin-notes.c\nindex 026cfd3..2e45be9 100644\n--- a/builtin-notes.c\n+++ b/builtin-notes.c\n@@ -447,6 +447,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \tint given_object = 0, i = 1, retval = 0;\n \tstruct msg_arg msg = { 0, 0, STRBUF_INIT };\n \tconst char *rewrite_cmd = NULL;\n+\tconst char *override_notes_ref = NULL;\n \tstruct option options[] = {\n \t\tOPT_GROUP(\"Notes options\"),\n \t\tOPT_CALLBACK('m', \"message\", &msg, \"MSG\",\n@@ -459,6 +460,8 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\t\t   \"reuse specified note object\", parse_reuse_arg),\n \t\tOPT_BOOLEAN('f', \"force\", &force, \"replace existing notes\"),\n \t\tOPT_BOOLEAN(0, \"stdin\", &from_stdin, \"read objects from stdin\"),\n+\t\tOPT_STRING(0, \"ref\", &override_notes_ref, \"notes_ref\",\n+\t\t\t   \"use notes from <notes_ref>\"),\n \t\tOPT_STRING(0, \"for-rewrite\", &rewrite_cmd, \"command\",\n \t\t\t   \"load rewriting config for <command> (implies --stdin)\"),\n \t\tOPT_END()\n@@ -468,6 +471,19 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, options, git_notes_usage, 0);\n \n+\tif (override_notes_ref) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tif (!prefixcmp(override_notes_ref, \"refs/notes/\"))\n+\t\t\t/* we're happy */;\n+\t\telse if (!prefixcmp(override_notes_ref, \"notes/\"))\n+\t\t\tstrbuf_addstr(&sb, \"refs/\");\n+\t\telse\n+\t\t\tstrbuf_addstr(&sb, \"refs/notes/\");\n+\t\tstrbuf_addstr(&sb, override_notes_ref);\n+\t\tsetenv(\"GIT_NOTES_REF\", sb.buf, 1);\n+\t\tstrbuf_release(&sb);\n+\t}\n+\n \tif (argc && !strcmp(argv[0], \"list\"))\n \t\tlist = 1;\n \telse if (argc && !strcmp(argv[0], \"add\"))\n-- \n1.7.0.218.g73a398\n"},{"id":"135384","messageId":"7v1vgcpyip.fsf@alter.siamese.dyndns.org","threadId":"22744","inReplyTo":"cover.1266885599.git.trast@student.ethz.ch","subject":"Re: [PATCH v5 00/11] several notes refs, post-rewrite, notes rewriting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-23T00:49:02Z","receivedAt":"2010-02-23T00:49:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n>> Yuck.  If you know what needs to be done, do that before other poeple add\n>> more options, please.\n>\n> *shrug*\n>\n> I was trying to be smart and save a call to git_config() when we know\n> we don't care about the config anyway.  After all it does read a bunch\n> of files.\n\nYou can set a flag to skip reading the trees (which is the expensive part)\nyou know you are not going to use to optimize out the expensive part of\nthe code, no?\n\n>> Perhaps I am not reading your code right in which case this part needs a\n>> bit more commenting?\n>\n> The catch is in the '&& t->ref'.\n\nAhh, Ok, so that was what I didn't see.  Perhaps it needs to say t->ref is\nset only under such and such conditions.  I am reasonably sure the next\nperson who needs to change this part of the code would appreciate such a\ncomment and that person may even be you 3 months from now.\n"},{"id":"135385","messageId":"201002230149.29817.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1266885599.git.trast@student.ethz.ch","subject":"Re: [PATCH v5 00/11] several notes refs, post-rewrite, notes rewriting","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T00:49:29Z","receivedAt":"2010-02-23T00:49:29Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Please ignore until I post a replacement for 2/11.  I managed to\nmis-edit *and* forget to test during the last round of fixups, and now\nit doesn't even compile.  I'll just go to bed and try again tomorrow.\n\nSorry.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"135389","messageId":"7vsk8sn2o3.fsf@alter.siamese.dyndns.org","threadId":"22744","inReplyTo":"3dbcdcf1a364d14968c07e99564acb232c6a5c43.1266885599.git.trast@student.ethz.ch","subject":"Re: [PATCH v5 02/11] Support showing notes from more than one notes tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-23T01:47:40Z","receivedAt":"2010-02-23T01:47:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> Changes since v4:\n> * introduce --show-notes=<ref> and --[no-]standard-notes\n\nNicer, much nicer.\n\n> * remove NOTES_SHOW_HEADER_WITH_REF distinction\n\nNote that there is a leftover caller that uses the symbol without noticing\nthat it has been retired.  I'll fix it up locally when I queue the series\nto 'pu'.\n\n> * parse config even if we don't care about it\n\nParsing is good, and not reading trees is very good.\n\nIt is silly to put yourself down after doing both of these very well by\nsaying \"even if we don't care about it\"---you obviously do care about\nmaking it earier for other people to build on top of your code.  Otherwise\nyou would have left the code unchanged from the previous round ;-).\n\n> diff --git a/notes.c b/notes.c\n> index 3ba3e6d..ee54a42 100644\n> --- a/notes.c\n> +++ b/notes.c\n> @@ -5,6 +5,8 @@\n>  #include \"utf8.h\"\n>  #include \"strbuf.h\"\n>  #include \"tree-walk.h\"\n> +#include \"string-list.h\"\n> +#include \"refs.h\"\n>  \n>  /*\n>   * Use a non-balancing simple 16-tree structure with struct int_node as\n> @@ -68,6 +70,9 @@ struct non_note {\n>  \n>  struct notes_tree default_notes_tree;\n>  \n> +struct string_list display_notes_refs;\n> +struct notes_tree **display_notes_trees;\n\nUnlike default_notes_tree, the above two can become static, as you made\nthe new logic better contained to this file and accessible only via\naccessor functions.\n\n> @@ -828,6 +833,76 @@ int combine_notes_ignore(unsigned char *cur_sha1,\n> ...\n> +static const char *default_notes_ref()\n\nI'll s/_ref()/_ref(void)/ here.\n\n> diff --git a/notes.h b/notes.h\n> index bad03cc..7650254 100644\n> --- a/notes.h\n> +++ b/notes.h\n> @@ -198,4 +198,22 @@ int for_each_note(struct notes_tree *t, int flags, each_note_fn fn,\n>  void format_note(struct notes_tree *t, const unsigned char *object_sha1,\n>  \t\tstruct strbuf *sb, const char *output_encoding, int flags);\n>  \n> +\n> +struct string_list;\n> +\n> +struct display_notes_opt\n> +{\n> +\tint suppress_default_notes : 1;\n> +\tstruct string_list *extra_notes_refs;\n> +};\n> +\n> +void init_display_notes(struct display_notes_opt *opt);\n> +void format_display_notes(const unsigned char *object_sha1,\n> +\t\t\t  struct strbuf *sb, const char *output_encoding, int flags);\n> +\n\nAs you are retiring format_note() as a public interface, and instead\nmaking format_display_notes() as the primary API for the callers, the\nformer should be made static to notes.c along with the large comment\ndescribing how to call it.  It may also be worth telling people how to\ncall this new public interface with similar comment here.\n\n> @@ -1096,8 +1096,8 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,\n>  \t\tstrbuf_addch(sb, '\\n');\n>  \n>  \tif (context->show_notes)\n> -\t\tformat_note(NULL, commit->object.sha1, sb, encoding,\n> -\t\t\t    NOTES_SHOW_HEADER | NOTES_INDENT);\n> +\t\tformat_display_notes(commit->object.sha1, sb, encoding,\n> +\t\t\t\t     NOTES_SHOW_HEADER_WITH_REF | NOTES_INDENT);\n\nI'll s/_WITH_REF// here.\n\n> diff --git a/revision.c b/revision.c\n> index 29721ec..d6e842e 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -1191,9 +1192,29 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n>  \t} else if (!strcmp(arg, \"--show-notes\")) {\n>  \t\trevs->show_notes = 1;\n>  \t\trevs->show_notes_given = 1;\n> +\t} else if (!prefixcmp(arg, \"--show-notes=\")) {\n> +\t\tstruct strbuf buf = STRBUF_INIT;\n> +\t\trevs->show_notes = 1;\n> +\t\trevs->show_notes_given = 1;\n> +\t\tif (!revs->notes_opt.extra_notes_refs)\n> +\t\t\trevs->notes_opt.extra_notes_refs = xcalloc(1, sizeof(struct string_list));\n> +\t\tif (!prefixcmp(arg+13, \"refs/\"))\n> +\t\t\t/* happy */;\n> +\t\telse if (!prefixcmp(arg+13, \"notes/\"))\n> +\t\t\tstrbuf_addstr(&buf, \"refs/\");\n> +\t\telse\n> +\t\t\tstrbuf_addstr(&buf, \"refs/notes/\");\n> +\t\tstrbuf_addstr(&buf, arg+13);\n> +\t\tstring_list_append(strbuf_detach(&buf, NULL),\n> +\t\t\t\t   revs->notes_opt.extra_notes_refs);\n\nNice; multiple --show-notes=... will accumulate in the order given.  I\nknew you won't be stupid to make this a colon separated string, but I had\nto double check ;-).\n"},{"id":"135440","messageId":"201002231810.18960.trast@student.ethz.ch","threadId":"22744","inReplyTo":"7vsk8sn2o3.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v5 02/11] Support showing notes from more than one notes tree","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T17:10:18Z","receivedAt":"2010-02-23T17:10:18Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"On Tuesday 23 February 2010 02:47:40 Junio C Hamano wrote:\n> Thomas Rast <trast@student.ethz.ch> writes:\n> > * remove NOTES_SHOW_HEADER_WITH_REF distinction\n> \n> Note that there is a leftover caller that uses the symbol without noticing\n> that it has been retired.  I'll fix it up locally when I queue the series\n> to 'pu'.\n\nThanks.  That was the breakage I hinted at in the last mail last\nnight; since it (hopefully) was the only problem with the series, I'll\ndispense with the replacement for now.\n\n> > @@ -68,6 +70,9 @@ struct non_note {\n> >  \n> >  struct notes_tree default_notes_tree;\n> >  \n> > +struct string_list display_notes_refs;\n> > +struct notes_tree **display_notes_trees;\n> \n> Unlike default_notes_tree, the above two can become static, as you made\n> the new logic better contained to this file and accessible only via\n> accessor functions.\n\nOh, so that's what you hinted at with the 'extern' question...\n\n> As you are retiring format_note() as a public interface, and instead\n> making format_display_notes() as the primary API for the callers, the\n\nI still need it for the \"format-patch from notes\" patch, to be posted\nsoonish.  So unless someone comes up with an alternative for that, it\nstays a public interface.\n\n> former should be made static to notes.c along with the large comment\n> describing how to call it.  It may also be worth telling people how to\n> call this new public interface with similar comment here.\n\nYou caught me on my comment laziness...  I suspect even the ones I can\nadd won't match Johan's excellent comments :-)\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"135445","messageId":"6cf9010742df96e0c68ef8adc1ab392c08525bc2.1266946262.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"201002231810.18960.trast@student.ethz.ch","subject":"[PATCH] format-patch: learn to fill comment section of email from notes","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T17:34:36Z","receivedAt":"2010-02-23T17:34:36Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Teach git-format-patch a new option --comment-notes (with config\nformat.commentNotes) which takes a notes ref argument.  These notes\nare then added to the patch email between the --- separator and the\ndiffstat/diff.\n\nThis is somewhat tricky because the pretty-printing code has no\ncontrol whatsoever over the --- and the diffstat, and there be dragons\nin the newline-producing code inside show_log().  Try not to disturb\nthem, and patch around only outside show_log().\n\nTo ensure correctness of the resulting email, extend the existing rule\n(separator if not 'oneline' format and --patch-with-stat) to also\ninclude a separator if there are notes and a patch will be included.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nSo here's the promised patch.  This is sort-of-RFC; it's not part of\nthe series because I don't want the latter to block on this one if\nanyone suggests changes.\n\nI'll follow up with a broken version to prove the point about the\ndragons I made above, if anyone wants to try fixing it.\n\n Documentation/config.txt           |    6 ++\n Documentation/git-format-patch.txt |   10 ++++\n builtin-log.c                      |   12 +++++\n log-tree.c                         |   14 +++++-\n pretty.c                           |    2 +-\n t/t4014-format-patch.sh            |   91 ++++++++++++++++++++++++++++++++++++\n 6 files changed, 133 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex dffe1f6..a9e27db 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -886,6 +886,12 @@ format.signoff::\n     the rights to submit this work under the same open source license.\n     Please see the 'SubmittingPatches' document for further discussion.\n \n+format.commentNotes::\n+\tThe default notes reference that is used for the comment\n+\tsection with --pretty=email and for 'git-format-patch'.  (The\n+\tcomment section is the area between the `\\---` separator and\n+\tthe patch, which also holds the diffstat.)\n+\n gc.aggressiveWindow::\n \tThe window size parameter used in the delta compression\n \talgorithm used by 'git gc --aggressive'.  This defaults\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 9674f9d..afe7e41 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -198,6 +198,16 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.\n \trange are always formatted as creation patches, independently\n \tof this flag.\n \n+--comment-notes=<ref>::\n+\tUse the notes from <ref> to fill the comment section of the\n+\temail, i.e., the part between the `\\---` separator and the\n+\tpatch.  See linkgit:git-notes[1].\n++\n+Warning: the code currently does not guard against a line in the notes\n+that starts with `diff`, which will be treated as the start of the\n+patch by 'git-am'.\n+\n+\n CONFIGURATION\n -------------\n You can specify extra mail header lines to be added to each message,\ndiff --git a/builtin-log.c b/builtin-log.c\nindex e7ea088..f85837d 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -19,6 +19,7 @@\n #include \"remote.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\n+#include \"notes.h\"\n \n /* Set a default date-time format for git log (\"log.date\" config variable) */\n static const char *default_date_mode = NULL;\n@@ -495,6 +496,7 @@ static void add_header(const char *value)\n #define THREAD_DEEP 2\n static int thread = 0;\n static int do_signoff = 0;\n+static const char *comment_notes = NULL;\n \n static int git_format_config(const char *var, const char *value, void *cb)\n {\n@@ -548,6 +550,9 @@ static int git_format_config(const char *var, const char *value, void *cb)\n \t\tdo_signoff = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(var, \"format.commentnotes\")) {\n+\t\treturn git_config_string(&comment_notes, var, value);\n+\t}\n \n \treturn git_log_config(var, value, cb);\n }\n@@ -955,6 +960,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t{ OPTION_CALLBACK, 0, \"thread\", &thread, \"style\",\n \t\t\t    \"enable message threading, styles: shallow, deep\",\n \t\t\t    PARSE_OPT_OPTARG, thread_callback },\n+\t\tOPT_STRING(0, \"comment-notes\", &comment_notes,\n+\t\t\t   \"ref\", \"use notes from <ref> for comment section\"),\n \t\tOPT_END()\n \t};\n \n@@ -1039,6 +1046,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tif (keep_subject && subject_prefix)\n \t\tdie (\"--subject-prefix and -k are mutually exclusive.\");\n \n+\tif (comment_notes) {\n+\t\tinit_notes(NULL, comment_notes, combine_notes_ignore, 0);\n+\t\trev.show_notes = 1;\n+\t}\n+\n \targc = setup_revisions(argc, argv, &rev, \"HEAD\");\n \tif (argc > 1)\n \t\tdie (\"unrecognized argument: %s\", argv[1]);\ndiff --git a/log-tree.c b/log-tree.c\nindex d3ae969..9830be8 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -7,6 +7,7 @@\n #include \"reflog-walk.h\"\n #include \"refs.h\"\n #include \"string-list.h\"\n+#include \"notes.h\"\n \n struct decoration name_decoration = { \"object names\" };\n \n@@ -457,6 +458,7 @@ int log_tree_diff_flush(struct rev_info *opt)\n \t}\n \n \tif (opt->loginfo && !opt->no_commit_id) {\n+\t\tconst unsigned char *sha1 = opt->loginfo->commit->object.sha1;\n \t\t/* When showing a verbose header (i.e. log message),\n \t\t * and not in --pretty=oneline format, we would want\n \t\t * an extra newline between the end of log and the\n@@ -467,10 +469,20 @@ int log_tree_diff_flush(struct rev_info *opt)\n \t\t    opt->verbose_header &&\n \t\t    opt->commit_format != CMIT_FMT_ONELINE) {\n \t\t\tint pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;\n-\t\t\tif ((pch & opt->diffopt.output_format) == pch)\n+\t\t\tif ((pch & opt->diffopt.output_format) == pch\n+\t\t\t    || (opt->commit_format == CMIT_FMT_EMAIL\n+\t\t\t\t&& opt->show_notes))\n \t\t\t\tprintf(\"---\");\n \t\t\tputchar('\\n');\n \t\t}\n+\t\tif (opt->commit_format == CMIT_FMT_EMAIL && opt->show_notes) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\t\tputchar('\\n');\n+\t\t\tformat_note(NULL, sha1, &sb, NULL, 0);\n+\t\t\tfwrite(sb.buf, 1, sb.len, stdout);\n+\t\t\tstrbuf_release(&sb);\n+\t\t\tputchar('\\n');\n+\t\t}\n \t}\n \tdiff_flush(&opt->diffopt);\n \treturn 1;\ndiff --git a/pretty.c b/pretty.c\nindex 6ba3da8..10d7812 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -1095,7 +1095,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,\n \tif (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)\n \t\tstrbuf_addch(sb, '\\n');\n \n-\tif (context->show_notes)\n+\tif (context->show_notes && fmt != CMIT_FMT_EMAIL)\n \t\tformat_display_notes(commit->object.sha1, sb, encoding,\n \t\t\t\t     NOTES_SHOW_HEADER | NOTES_INDENT);\n \ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex f2a2aaa..653e50a 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -557,4 +557,95 @@ test_expect_success 'format-patch -- <path>' '\n \t! grep \"Use .--\" error\n '\n \n+cat >expect-no-comment <<EOF\n+Subject: [PATCH] foo\n+Cc: R. E. Cipient <rcipient@example.com>\n+\n+---\n+ file |   32 --------------------------------\n+ foo  |   32 ++++++++++++++++++++++++++++++++\n+ 2 files changed, 32 insertions(+), 32 deletions(-)\n+ delete mode 100644 file\n+ create mode 100644 foo\n+\n+diff --git a/file b/file\n+EOF\n+\n+cat >expect-comment <<EOF\n+Subject: [PATCH] foo\n+Cc: R. E. Cipient <rcipient@example.com>\n+\n+---\n+\n+a bit of testing here\n+\n+ file |   32 --------------------------------\n+ foo  |   32 ++++++++++++++++++++++++++++++++\n+ 2 files changed, 32 insertions(+), 32 deletions(-)\n+ delete mode 100644 file\n+ create mode 100644 foo\n+\n+diff --git a/file b/file\n+EOF\n+\n+test_expect_success 'comment section untouched by default' '\n+\tgit notes add -m\"a bit of testing here\" &&\n+\tgit format-patch -1 --stdout > output &&\n+\tsed -e \"1,/^Date/d\" -e \"/^diff --git/q\" output > output+ &&\n+\ttest_cmp expect-no-comment output+\n+'\n+\n+test_expect_success '--comment-notes' '\n+\tgit format-patch --comment-notes=refs/notes/commits -1 --stdout > output &&\n+\tsed -e \"1,/^Date/d\" -e \"/^diff --git/q\" output > output+ &&\n+\ttest_cmp expect-comment output+\n+'\n+\n+test_expect_success 'format.commentNotes' '\n+\tgit config format.commentNotes refs/notes/commits &&\n+\tgit format-patch -1 --stdout > output &&\n+\tsed -e \"1,/^Date/d\" -e \"/^diff --git/q\" output > output+ &&\n+\ttest_cmp expect-comment output+\n+'\n+\n+test_expect_success '--comment-notes overrides format.commentNotes' '\n+\tgit config format.commentNotes refs/notes/empty &&\n+\tgit format-patch --comment-notes=refs/notes/commits -1 --stdout > output &&\n+\tsed -e \"1,/^Date/d\" -e \"/^diff --git/q\" output > output+ &&\n+\ttest_cmp expect-comment output+\n+'\n+\n+cat >expect <<EOF\n+Subject: [PATCH] foo\n+Cc: R. E. Cipient <rcipient@example.com>\n+\n+\n+diff --git a/file b/file\n+EOF\n+\n+test_expect_success 'no separator with neither diffstat nor notes' '\n+\tgit config --unset format.commentNotes &&\n+\tgit format-patch -p -1 --stdout > output &&\n+\tsed -e \"1,/^Date/d\" -e \"/^diff --git/q\" output > output+ &&\n+\ttest_cmp expect output+\n+'\n+\n+cat >expect <<EOF\n+Subject: [PATCH] foo\n+Cc: R. E. Cipient <rcipient@example.com>\n+\n+---\n+\n+a bit of testing here\n+\n+diff --git a/file b/file\n+EOF\n+\n+test_expect_success 'separator with notes but no diffstat' '\n+\tgit config format.commentNotes refs/notes/commits &&\n+\tgit format-patch -p -1 --stdout > output &&\n+\tsed -e \"1,/^Date/d\" -e \"/^diff --git/q\" output > output+ &&\n+\ttest_cmp expect output+\n+'\n+\n test_done\n-- \n1.7.0.218.g73a398\n"},{"id":"135446","messageId":"fe0d69eba1b0265c0ca913831ba26e643062f8eb.1266946248.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"6cf9010742df96e0c68ef8adc1ab392c08525bc2.1266946262.git.trast@student.ethz.ch","subject":"[PATCH] BROKEN -- format-patch: learn to fill comment section of email from notes","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T17:34:56Z","receivedAt":"2010-02-23T17:34:56Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Teach git-format-patch a new option --comment-notes (with config\nformat.commentNotes) which takes a notes ref argument.  These notes\nare then added to the patch email between the --- separator and the\ndiffstat/diff.\n\nThis is somewhat tricky because the pretty-printing code has no\ncontrol whatsoever over the --- and the diffstat.  To fix that, move\nthe associated code down into show_log(), which can set options in the\npretty_print_context.  Extend the latter with fields for an arbitrary\nstring, to be inserted after commit message and after notes.  Use\nthose to place the --- appropriately.\n\nTo ensure correctness of the resulting email, extend the existing rule\n(separator if not 'oneline' format and --patch-with-stat) to also\ninclude a separator if there are notes and a patch will be included.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\n\n Documentation/config.txt           |    6 ++\n Documentation/git-format-patch.txt |   10 ++++\n builtin-log.c                      |   12 +++++\n commit.h                           |    2 +\n log-tree.c                         |   38 ++++++++++-----\n pretty.c                           |   19 ++++++-\n t/t4014-format-patch.sh            |   91 ++++++++++++++++++++++++++++++++++++\n 7 files changed, 162 insertions(+), 16 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex dffe1f6..a9e27db 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -886,6 +886,12 @@ format.signoff::\n     the rights to submit this work under the same open source license.\n     Please see the 'SubmittingPatches' document for further discussion.\n \n+format.commentNotes::\n+\tThe default notes reference that is used for the comment\n+\tsection with --pretty=email and for 'git-format-patch'.  (The\n+\tcomment section is the area between the `\\---` separator and\n+\tthe patch, which also holds the diffstat.)\n+\n gc.aggressiveWindow::\n \tThe window size parameter used in the delta compression\n \talgorithm used by 'git gc --aggressive'.  This defaults\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 9674f9d..afe7e41 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -198,6 +198,16 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.\n \trange are always formatted as creation patches, independently\n \tof this flag.\n \n+--comment-notes=<ref>::\n+\tUse the notes from <ref> to fill the comment section of the\n+\temail, i.e., the part between the `\\---` separator and the\n+\tpatch.  See linkgit:git-notes[1].\n++\n+Warning: the code currently does not guard against a line in the notes\n+that starts with `diff`, which will be treated as the start of the\n+patch by 'git-am'.\n+\n+\n CONFIGURATION\n -------------\n You can specify extra mail header lines to be added to each message,\ndiff --git a/builtin-log.c b/builtin-log.c\nindex e7ea088..f85837d 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -19,6 +19,7 @@\n #include \"remote.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\n+#include \"notes.h\"\n \n /* Set a default date-time format for git log (\"log.date\" config variable) */\n static const char *default_date_mode = NULL;\n@@ -495,6 +496,7 @@ static void add_header(const char *value)\n #define THREAD_DEEP 2\n static int thread = 0;\n static int do_signoff = 0;\n+static const char *comment_notes = NULL;\n \n static int git_format_config(const char *var, const char *value, void *cb)\n {\n@@ -548,6 +550,9 @@ static int git_format_config(const char *var, const char *value, void *cb)\n \t\tdo_signoff = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(var, \"format.commentnotes\")) {\n+\t\treturn git_config_string(&comment_notes, var, value);\n+\t}\n \n \treturn git_log_config(var, value, cb);\n }\n@@ -955,6 +960,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t{ OPTION_CALLBACK, 0, \"thread\", &thread, \"style\",\n \t\t\t    \"enable message threading, styles: shallow, deep\",\n \t\t\t    PARSE_OPT_OPTARG, thread_callback },\n+\t\tOPT_STRING(0, \"comment-notes\", &comment_notes,\n+\t\t\t   \"ref\", \"use notes from <ref> for comment section\"),\n \t\tOPT_END()\n \t};\n \n@@ -1039,6 +1046,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tif (keep_subject && subject_prefix)\n \t\tdie (\"--subject-prefix and -k are mutually exclusive.\");\n \n+\tif (comment_notes) {\n+\t\tinit_notes(NULL, comment_notes, combine_notes_ignore, 0);\n+\t\trev.show_notes = 1;\n+\t}\n+\n \targc = setup_revisions(argc, argv, &rev, \"HEAD\");\n \tif (argc > 1)\n \t\tdie (\"unrecognized argument: %s\", argv[1]);\ndiff --git a/commit.h b/commit.h\nindex 3cf5166..44bcf5a 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -68,6 +68,8 @@ struct pretty_print_context\n \tint abbrev;\n \tconst char *subject;\n \tconst char *after_subject;\n+\tconst char *after_message;\n+\tconst char *after_notes;\n \tenum date_mode date_mode;\n \tint need_8bit_cte;\n \tint show_notes;\ndiff --git a/log-tree.c b/log-tree.c\nindex d3ae969..93e0f48 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -7,6 +7,7 @@\n #include \"reflog-walk.h\"\n #include \"refs.h\"\n #include \"string-list.h\"\n+#include \"notes.h\"\n \n struct decoration name_decoration = { \"object names\" };\n \n@@ -413,6 +414,30 @@ void show_log(struct rev_info *opt)\n \tctx.abbrev = opt->diffopt.abbrev;\n \tctx.after_subject = extra_headers;\n \tctx.reflog_info = opt->reflog_info;\n+\n+\tctx.after_message = NULL;\n+\t/*\n+\t * In formats other than 'oneline', add an extra newline.  In\n+\t * addition, add the triple-dash separator between message and\n+\t * diff if there is a diffstat coming or we are showing notes\n+\t * in 'email' format.  Otherwise, 'git am' would include the\n+\t * diffstat or notes in the commit message when applying the\n+\t * commit.\n+\t */\n+\tif ((opt->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) &&\n+\t    opt->verbose_header &&\n+\t    opt->commit_format != CMIT_FMT_ONELINE) {\n+\t\tint pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;\n+\t\tint patch_with_stat = (pch & opt->diffopt.output_format) == pch;\n+\t\tif (opt->commit_format == CMIT_FMT_EMAIL\n+\t\t    && (opt->show_notes || patch_with_stat))\n+\t\t\tctx.after_message = \"---\\n\";\n+\t\telse if (opt->commit_format != CMIT_FMT_EMAIL && patch_with_stat)\n+\t\t\tctx.after_notes = \"---\\n\";\n+\t\telse\n+\t\t\tctx.after_notes = \"\\n\";\n+\t}\n+\n \tpretty_print_commit(opt->commit_format, commit, &msgbuf, &ctx);\n \n \tif (opt->add_signoff)\n@@ -457,20 +482,7 @@ int log_tree_diff_flush(struct rev_info *opt)\n \t}\n \n \tif (opt->loginfo && !opt->no_commit_id) {\n-\t\t/* When showing a verbose header (i.e. log message),\n-\t\t * and not in --pretty=oneline format, we would want\n-\t\t * an extra newline between the end of log and the\n-\t\t * output for readability.\n-\t\t */\n \t\tshow_log(opt);\n-\t\tif ((opt->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) &&\n-\t\t    opt->verbose_header &&\n-\t\t    opt->commit_format != CMIT_FMT_ONELINE) {\n-\t\t\tint pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;\n-\t\t\tif ((pch & opt->diffopt.output_format) == pch)\n-\t\t\t\tprintf(\"---\");\n-\t\t\tputchar('\\n');\n-\t\t}\n \t}\n \tdiff_flush(&opt->diffopt);\n \treturn 1;\ndiff --git a/pretty.c b/pretty.c\nindex 6ba3da8..4fc855e 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -1095,9 +1095,22 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,\n \tif (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)\n \t\tstrbuf_addch(sb, '\\n');\n \n-\tif (context->show_notes)\n-\t\tformat_display_notes(commit->object.sha1, sb, encoding,\n-\t\t\t\t     NOTES_SHOW_HEADER | NOTES_INDENT);\n+\tif (context->after_message)\n+\t\tstrbuf_addstr(sb, context->after_message);\n+\n+\tif (context->show_notes) {\n+\t\tif (fmt == CMIT_FMT_EMAIL) {\n+\t\t\tstrbuf_addch(sb, '\\n');\n+\t\t\tformat_note(NULL, commit->object.sha1, sb, encoding, 0);\n+\t\t\tstrbuf_addch(sb, '\\n');\n+\t\t} else {\n+\t\t\tformat_display_notes(commit->object.sha1, sb, encoding,\n+\t\t\t\t\t     NOTES_SHOW_HEADER | NOTES_INDENT);\n+\t\t}\n+\t}\n+\n+\tif (context->after_notes)\n+\t\tstrbuf_addstr(sb, context->after_notes);\n \n \tfree(reencoded);\n }\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex f2a2aaa..653e50a 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -557,4 +557,95 @@ test_expect_success 'format-patch -- <path>' '\n \t! grep \"Use .--\" error\n '\n \n+cat >expect-no-comment <<EOF\n+Subject: [PATCH] foo\n+Cc: R. E. Cipient <rcipient@example.com>\n+\n+---\n+ file |   32 --------------------------------\n+ foo  |   32 ++++++++++++++++++++++++++++++++\n+ 2 files changed, 32 insertions(+), 32 deletions(-)\n+ delete mode 100644 file\n+ create mode 100644 foo\n+\n+diff --git a/file b/file\n+EOF\n+\n+cat >expect-comment <<EOF\n+Subject: [PATCH] foo\n+Cc: R. E. Cipient <rcipient@example.com>\n+\n+---\n+\n+a bit of testing here\n+\n+ file |   32 --------------------------------\n+ foo  |   32 ++++++++++++++++++++++++++++++++\n+ 2 files changed, 32 insertions(+), 32 deletions(-)\n+ delete mode 100644 file\n+ create mode 100644 foo\n+\n+diff --git a/file b/file\n+EOF\n+\n+test_expect_success 'comment section untouched by default' '\n+\tgit notes add -m\"a bit of testing here\" &&\n+\tgit format-patch -1 --stdout > output &&\n+\tsed -e \"1,/^Date/d\" -e \"/^diff --git/q\" output > output+ &&\n+\ttest_cmp expect-no-comment output+\n+'\n+\n+test_expect_success '--comment-notes' '\n+\tgit format-patch --comment-notes=refs/notes/commits -1 --stdout > output &&\n+\tsed -e \"1,/^Date/d\" -e \"/^diff --git/q\" output > output+ &&\n+\ttest_cmp expect-comment output+\n+'\n+\n+test_expect_success 'format.commentNotes' '\n+\tgit config format.commentNotes refs/notes/commits &&\n+\tgit format-patch -1 --stdout > output &&\n+\tsed -e \"1,/^Date/d\" -e \"/^diff --git/q\" output > output+ &&\n+\ttest_cmp expect-comment output+\n+'\n+\n+test_expect_success '--comment-notes overrides format.commentNotes' '\n+\tgit config format.commentNotes refs/notes/empty &&\n+\tgit format-patch --comment-notes=refs/notes/commits -1 --stdout > output &&\n+\tsed -e \"1,/^Date/d\" -e \"/^diff --git/q\" output > output+ &&\n+\ttest_cmp expect-comment output+\n+'\n+\n+cat >expect <<EOF\n+Subject: [PATCH] foo\n+Cc: R. E. Cipient <rcipient@example.com>\n+\n+\n+diff --git a/file b/file\n+EOF\n+\n+test_expect_success 'no separator with neither diffstat nor notes' '\n+\tgit config --unset format.commentNotes &&\n+\tgit format-patch -p -1 --stdout > output &&\n+\tsed -e \"1,/^Date/d\" -e \"/^diff --git/q\" output > output+ &&\n+\ttest_cmp expect output+\n+'\n+\n+cat >expect <<EOF\n+Subject: [PATCH] foo\n+Cc: R. E. Cipient <rcipient@example.com>\n+\n+---\n+\n+a bit of testing here\n+\n+diff --git a/file b/file\n+EOF\n+\n+test_expect_success 'separator with notes but no diffstat' '\n+\tgit config format.commentNotes refs/notes/commits &&\n+\tgit format-patch -p -1 --stdout > output &&\n+\tsed -e \"1,/^Date/d\" -e \"/^diff --git/q\" output > output+ &&\n+\ttest_cmp expect output+\n+'\n+\n test_done\n-- \n1.7.0.218.g73a398\n"},{"id":"135447","messageId":"201002231837.11245.trast@student.ethz.ch","threadId":"22744","inReplyTo":"fe0d69eba1b0265c0ca913831ba26e643062f8eb.1266946248.git.trast@student.ethz.ch","subject":"Re: [PATCH] BROKEN -- format-patch: learn to fill comment section of email from notes","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-23T17:37:10Z","receivedAt":"2010-02-23T17:37:10Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"On Tuesday 23 February 2010 18:34:56 Thomas Rast wrote:\n> [PATCH] BROKEN -- format-patch: learn to fill comment section of email from notes\n\nBTW, I forgot to say: the breakage is visible in either t4013 or\nt4014, depending which way you tweak the newlines.\n\nIIRC there's also a problem with this patch and Signed-off-by\nhandling, which I didn't bother fixing because I couldn't get the\nnewlines right.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"135478","messageId":"7vmxyzehug.fsf@alter.siamese.dyndns.org","threadId":"22744","inReplyTo":"6cf9010742df96e0c68ef8adc1ab392c08525bc2.1266946262.git.trast@student.ethz.ch","subject":"Re: [PATCH] format-patch: learn to fill comment section of email from notes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-23T21:56:55Z","receivedAt":"2010-02-23T21:56:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> Teach git-format-patch a new option --comment-notes (with config\n> format.commentNotes) which takes a notes ref argument.  These notes\n> are then added to the patch email between the --- separator and the\n> diffstat/diff.\n\nHmmm, why do I find this an ugly hack?\n\nYou already have a nice \"format_display_notes()\" infrastructure to allow\nusers to get notes from arbitrary notes namespaces, yet this limits the\nuser to a single notes namespace.  What was the infrastructure built for\nif not used in places like this uniformly?\n\nIf the answer is \"because notes.displayref is a configuration and it is\ncumbersome to change every time\", then I don't have a sympathy ;-) as that\nis exactly why I said config without command line override is a bad thing.\n\n> diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\n> index 9674f9d..afe7e41 100644\n> --- a/Documentation/git-format-patch.txt\n> +++ b/Documentation/git-format-patch.txt\n> @@ -198,6 +198,16 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.\n>  \trange are always formatted as creation patches, independently\n>  \tof this flag.\n>  \n> +--comment-notes=<ref>::\n> +\tUse the notes from <ref> to fill the comment section of the\n> +\temail, i.e., the part between the `\\---` separator and the\n> +\tpatch.  See linkgit:git-notes[1].\n> ++\n> +Warning: the code currently does not guard against a line in the notes\n> +that starts with `diff`, which will be treated as the start of the\n> +patch by 'git-am'.\n\nIt is customary to indent the material after --- by one place (or more) so\nit probably is a good idea to do that for notes as well, if we are going\nto put it after the three dash lines.  Notice that diffstat output is\nalready indented that way ;-).\n\n> @@ -457,6 +458,7 @@ int log_tree_diff_flush(struct rev_info *opt)\n>  \t}\n>  \n>  \tif (opt->loginfo && !opt->no_commit_id) {\n> +\t\tconst unsigned char *sha1 = opt->loginfo->commit->object.sha1;\n>  \t\t/* When showing a verbose header (i.e. log message),\n>  \t\t * and not in --pretty=oneline format, we would want\n>  \t\t * an extra newline between the end of log and the\n> @@ -467,10 +469,20 @@ int log_tree_diff_flush(struct rev_info *opt)\n>  \t\t    opt->verbose_header &&\n>  \t\t    opt->commit_format != CMIT_FMT_ONELINE) {\n>  \t\t\tint pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;\n> -\t\t\tif ((pch & opt->diffopt.output_format) == pch)\n> +\t\t\tif ((pch & opt->diffopt.output_format) == pch\n> +\t\t\t    || (opt->commit_format == CMIT_FMT_EMAIL\n> +\t\t\t\t&& opt->show_notes))\n\nThis adds a lot of logic to a code that used to be simple \"if we have more\nstuff to add after message, delimit with \"---\\n\"\".  Perhaps the whole body\nof the \"if\" statement ll. 460- should be made into a static helper\nfunction with a single callsite that a clever compiler would inline.\n\nAlso I think you would emit \"---\" even if the commit in question does not\nhappen to have note with this code.\n\nI've attached a \"how about doing it this way\" weatherbaloon patch to be\napplied on top of this patch at the end---I didn't bother to indent the\nnotes text nor change it to use format_display_notes(), but hopefully you\nwill agree the code structure would be easier to follow this way.\n\n> diff --git a/pretty.c b/pretty.c\n> index 6ba3da8..10d7812 100644\n> --- a/pretty.c\n> +++ b/pretty.c\n> @@ -1095,7 +1095,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,\n>  \tif (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)\n>  \t\tstrbuf_addch(sb, '\\n');\n>  \n> -\tif (context->show_notes)\n> +\tif (context->show_notes && fmt != CMIT_FMT_EMAIL)\n>  \t\tformat_display_notes(commit->object.sha1, sb, encoding,\n>  \t\t\t\t     NOTES_SHOW_HEADER | NOTES_INDENT);\n\nThis is a good fix to prevent notes from getting injected above the\nseparator line, regardless of your series, I think.\n\n log-tree.c |   71 +++++++++++++++++++++++++++++++++++++++--------------------\n 1 files changed, 47 insertions(+), 24 deletions(-)\n\ndiff --git a/log-tree.c b/log-tree.c\nindex 9baf306..af65d33 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -445,6 +445,52 @@ void show_log(struct rev_info *opt)\n \tstrbuf_release(&msgbuf);\n }\n \n+static void log_tree_after_message(struct rev_info *opt)\n+{\n+\tconst int pch = (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_PATCH);\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t/*\n+\t * When showing a verbose header (i.e. log message),\n+\t * and not in --pretty=oneline format, we would want\n+\t * an extra newline between the end of log and the\n+\t * output for readability.\n+\t */\n+\tif (! ((opt->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) &&\n+\t       opt->verbose_header &&\n+\t       opt->commit_format != CMIT_FMT_ONELINE) )\n+\t\treturn;\n+\n+\t/*\n+\t * Prepare notes if any...\n+\t */\n+\tif (opt->commit_format == CMIT_FMT_EMAIL && opt->show_notes)\n+\t\tformat_note(NULL, opt->loginfo->commit->object.sha1,\n+\t\t\t    &sb, NULL, 0);\n+\n+\t/*\n+\t * Will we have something other than the message itself?  If\n+\t * so we would need three-dashes.\n+\t */\n+\tif (((pch & opt->diffopt.output_format) == pch) || sb.len)\n+\t\tprintf(\"---\");\n+\n+\t/*\n+\t * Then the promised newline...\n+\t */\n+\tputchar('\\n');\n+\n+\t/*\n+\t * And finally the notes...\n+\t */\n+\tif (sb.len) {\n+\t\tputchar('\\n');\n+\t\tfwrite(sb.buf, 1, sb.len, stdout);\n+\t\tstrbuf_release(&sb);\n+\t\tputchar('\\n');\n+\t}\n+}\n+\n int log_tree_diff_flush(struct rev_info *opt)\n {\n \tdiffcore_std(&opt->diffopt);\n@@ -458,31 +504,8 @@ int log_tree_diff_flush(struct rev_info *opt)\n \t}\n \n \tif (opt->loginfo && !opt->no_commit_id) {\n-\t\tconst unsigned char *sha1 = opt->loginfo->commit->object.sha1;\n-\t\t/* When showing a verbose header (i.e. log message),\n-\t\t * and not in --pretty=oneline format, we would want\n-\t\t * an extra newline between the end of log and the\n-\t\t * output for readability.\n-\t\t */\n \t\tshow_log(opt);\n-\t\tif ((opt->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) &&\n-\t\t    opt->verbose_header &&\n-\t\t    opt->commit_format != CMIT_FMT_ONELINE) {\n-\t\t\tint pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;\n-\t\t\tif ((pch & opt->diffopt.output_format) == pch\n-\t\t\t    || (opt->commit_format == CMIT_FMT_EMAIL\n-\t\t\t\t&& opt->show_notes))\n-\t\t\t\tprintf(\"---\");\n-\t\t\tputchar('\\n');\n-\t\t}\n-\t\tif (opt->commit_format == CMIT_FMT_EMAIL && opt->show_notes) {\n-\t\t\tstruct strbuf sb = STRBUF_INIT;\n-\t\t\tputchar('\\n');\n-\t\t\tformat_note(NULL, sha1, &sb, NULL, 0);\n-\t\t\tfwrite(sb.buf, 1, sb.len, stdout);\n-\t\t\tstrbuf_release(&sb);\n-\t\t\tputchar('\\n');\n-\t\t}\n+\t\tlog_tree_after_message(opt);\n \t}\n \tdiff_flush(&opt->diffopt);\n \treturn 1;\n"},{"id":"135527","messageId":"7vaauzcg7l.fsf@alter.siamese.dyndns.org","threadId":"22744","inReplyTo":"87f20b987fa7a389e89f8931d553de7095d2bc86.1266885599.git.trast@student.ethz.ch","subject":"Re: [PATCH v5 06/11] rebase -i: invoke post-rewrite hook","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-24T06:15:10Z","receivedAt":"2010-02-24T06:15:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> +test_expect_success 'git rebase -i (unchanged)' '\n> +\tgit reset --hard D &&\n> +\tclear_hook_input &&\n> +\tFAKE_LINES=\"1 2\" test_must_fail git rebase -i --onto A B &&\n\nThis\n\n> ...\n> +test_expect_success 'git rebase -i (skip)' '\n> +\tgit reset --hard D &&\n> +\tclear_hook_input &&\n> +\tFAKE_LINES=\"2\" test_must_fail git rebase -i --onto A B &&\n\nand this\n\n> ...\n> +test_expect_success 'git rebase -i (squash)' '\n> +\tgit reset --hard D &&\n> +\tclear_hook_input &&\n> +\tFAKE_LINES=\"1 squash 2\" test_must_fail git rebase -i --onto A B &&\n\nand this need to be fixed to something like\n\n\t( FAKE_LINES=... &&\n          export FAKE_LINES &&\n          test_must_fail git ... ) &&\n\nfor Bourne-shell portability.  test_must_fail is a shell function and the\nusual one-shot-assignment-to-export rule does not apply.\n"},{"id":"135541","messageId":"4B84D92A.8060804@gmail.com","threadId":"22744","inReplyTo":"201002231837.11245.trast@student.ethz.ch","subject":"Re: [PATCH] BROKEN -- format-patch: learn to fill comment section of email from notes","fromName":"Stephen Boyd","fromEmail":"bebarino@gmail.com","sentAt":"2010-02-24T07:45:46Z","receivedAt":"2010-02-24T07:45:46Z","isPatch":true,"sender":{"key":"bebarino@gmail.com","avatar":"https://avatars.githubusercontent.com/u/38832?v=4"},"body":"On 02/23/2010 09:37 AM, Thomas Rast wrote:\n> On Tuesday 23 February 2010 18:34:56 Thomas Rast wrote:\n>> [PATCH] BROKEN -- format-patch: learn to fill comment section of email from notes\n>\n> BTW, I forgot to say: the breakage is visible in either t4013 or\n> t4014, depending which way you tweak the newlines.\n\nLast time I tried to touch the code in this area I gave up. It looks\nlike you're experiencing the same problem where pretty formats cause\nproblems in the tests. I would love to see this sorted out so I can\nresurrect my dead topic \"log-tree: always add --- marker when options\nare patch and a stat\".\n\nGood luck!\n"},{"id":"135650","messageId":"7vd3zurmoa.fsf@alter.siamese.dyndns.org","threadId":"22744","inReplyTo":"4623296da9ed82447718b46c72e6a22ff0152fc0.1266885599.git.trast@student.ethz.ch","subject":"Re: [PATCH v5 09/11] rebase: support automatic notes copying","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-25T03:58:45Z","receivedAt":"2010-02-25T03:58:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> Luckily, all the support already happens to be there.\n>\n> Signed-off-by: Thomas Rast <trast@student.ethz.ch>\n\nI did my first \"rebase -i / commit --amend / rebase --continue\" cycle with\nthis series, and it correctly carried notes I keep track of Message-Id of\noriginal patches with.  Good job.\n\nAfter looking at the output from \"git log refs/notes/amlog\", I have a few\nobservations, though.\n\n - \"git notes add\" and friends honor GIT_COMMITER_* and GIT_AUTHOR_* as\n   usual.  Since I use post-applypatch hook to record the message-id from\n   \"git am\", I ended up getting my notes written by original submitters.\n\n - The log messages are mostly useless.  The scripted implementation said\n   \"Annotate 6d8094a8aa32c30ff39c6b8609acb8d057ccb5e5\" which was not the\n   most informative, but the new one says \"Notes added by 'git notes add'\".\n\nI haven't thought things through thoroughly, and I am not complaining, but\nit may be worth thinking about what the \"commit\"-ness of generations of\nnotes tree _should_ mean.\n\nAs we are recording the history of how the set of my notes have grown over\ntime, I think it is natural to expect that the user can record who is\nrecording this notes tree at what time for what reason, just like a\nregular commit does.\n\n - \"added by 'git notes add'\" is just as immaterial as \"commit log edited\n   with vi\".  'copied via notes.rebase.rewrite hook' would make much more\n   sense.\n\n - \"git notes add\" already uses -m option to take the text used as note\n   (i.e. payload of the commit); the user may need a way to affect the\n   commit log message to record why the note is added (or modified).\n\n - As to the determination of committer/author identity, I think what the\n   code currently does (i.e. honor the environment and user.* config) is\n   perfectly sane, but at the same time I think it would be surprising for\n   unsuspecting users.  We may want to advise users about this in the\n   documentation.\n\nBut the above is merely my personal feeling on the \"notes history\".\n\nThe notes implementation (from the scripted one to the one in 'pu') seems\nto take quite a different view and is designed as if aspects other than\nthe topology and the tree each \"commit\" object records are not useful at\nall, and commits are used to implement the notes history only because the\nancestry might help when we later implement merges of notes histories.  I\nthink that could also be a _valid_ position to take.\n"},{"id":"136512","messageId":"cover.1268229087.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"7vd3zurmoa.fsf@alter.siamese.dyndns.org","subject":"[PATCH v6 00/13] several notes refs, post-rewrite, notes rewriting","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-03-10T14:03:35Z","receivedAt":"2010-03-10T14:03:35Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Hi all,\n\nHere's (finally) the reroll.  Sorry for the long wait, I was been\nswamped at work up until yesterday :-(\n\nI hope I collected all changes that Junio requested; of note are\n\n* the new interface in notes.h actually comes with comments,\n\n* doc update to git-notes(1) in the patch that introduces\n  notes.displayRef\n\nI'm not aware of comments by anyone else, so if I missed them please\npoint them out.\n\nI did some other tweaks, including the new patch 12 that in particular\navoids creating any new commits for notes refs that exist but did not\nhave anything copied, if you have copying enabled.\n\n\nOn Thursday 25 February 2010 04:58:45 Junio C Hamano wrote:\n>  - The log messages are mostly useless.  The scripted implementation said\n>    \"Annotate 6d8094a8aa32c30ff39c6b8609acb8d057ccb5e5\" which was not the\n>    most informative, but the new one says \"Notes added by 'git notes add'\".\n\nI hesitated making it \"more informative\" in the direction of \"Annotate\n$sha1\" because the same answer can be found with a simple 'git log\n--stat'.  So I consider the \"Notes added by '<command>'\" to actually\nbe more useful since it conveys something that is not readily\navailable.\n\nThat being said, I think\n\n>  - \"git notes add\" already uses -m option to take the text used as note\n>    (i.e. payload of the commit); the user may need a way to affect the\n>    commit log message to record why the note is added (or modified).\n\nis a good idea, even if only to let people write down which\nnote-adding script recorded this note.\n\n\n>  - \"git notes add\" and friends honor GIT_COMMITER_* and GIT_AUTHOR_* as\n>    usual.  Since I use post-applypatch hook to record the message-id from\n>    \"git am\", I ended up getting my notes written by original submitters.\n[...]\n>  - As to the determination of committer/author identity, I think what the\n>    code currently does (i.e. honor the environment and user.* config) is\n>    perfectly sane, but at the same time I think it would be surprising for\n>    unsuspecting users.  We may want to advise users about this in the\n>    documentation.\n\nI added a small patch that writes this and the history recording down\nin git-notes(1).  However, I'm hesitating to directly say \"and this\nwill bite you if you use it in post-applypatch\"; isn't the latter an\nimplementation detail of git-am?\n\n\n\nThomas Rast (13):\n  test-lib: unset GIT_NOTES_REF to stop it from influencing tests\n  Support showing notes from more than one notes tree\n  Documentation: document post-rewrite hook\n  commit --amend: invoke post-rewrite hook\n  rebase: invoke post-rewrite hook\n  rebase -i: invoke post-rewrite hook\n  notes: implement 'git notes copy --stdin'\n  notes: implement helpers needed for note copying during rewrite\n  rebase: support automatic notes copying\n  commit --amend: copy notes to the new commit\n  notes: add shorthand --ref to override GIT_NOTES_REF\n  notes: track whether notes_trees were changed at all\n  git-notes(1): add a section about the meaning of history\n\n Documentation/config.txt         |   53 +++++-\n Documentation/git-notes.txt      |   46 ++++-\n Documentation/githooks.txt       |   38 ++++\n Documentation/pretty-options.txt |   11 +-\n builtin.h                        |   18 ++\n builtin/commit.c                 |   45 +++++\n builtin/log.c                    |    2 +\n builtin/notes.c                  |  197 ++++++++++++++++++++-\n cache.h                          |    3 +\n git-am.sh                        |   13 ++\n git-rebase--interactive.sh       |   52 +++++-\n git-rebase.sh                    |    6 +\n notes.c                          |  195 +++++++++++++++++++-\n notes.h                          |   67 +++++++\n pretty.c                         |    6 +-\n refs.c                           |    4 +-\n refs.h                           |    5 +\n revision.c                       |   21 ++\n revision.h                       |    5 +\n t/t3301-notes.sh                 |  377 +++++++++++++++++++++++++++++++++++++-\n t/t3400-rebase.sh                |   17 ++\n t/t3404-rebase-interactive.sh    |   24 +++\n t/t5407-post-rewrite-hook.sh     |  183 ++++++++++++++++++\n t/t7501-commit.sh                |   12 ++\n t/test-lib.sh                    |    4 +\n 25 files changed, 1371 insertions(+), 33 deletions(-)\n create mode 100755 t/t5407-post-rewrite-hook.sh\n"},{"id":"136513","messageId":"31001e0ddcf756d542e2c899f61c78e25beaf6fa.1268229087.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1268229087.git.trast@student.ethz.ch","subject":"[PATCH v6 01/13] test-lib: unset GIT_NOTES_REF to stop it from influencing tests","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-03-10T14:03:38Z","receivedAt":"2010-03-10T14:03:38Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"---\n\n\n t/test-lib.sh |    1 +\n 1 files changed, 1 insertions(+), 0 deletions(-)\n\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex a0e396a..400bc6b 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -54,6 +54,7 @@ unset GIT_OBJECT_DIRECTORY\n unset GIT_CEILING_DIRECTORIES\n unset SHA1_FILE_DIRECTORIES\n unset SHA1_FILE_DIRECTORY\n+unset GIT_NOTES_REF\n GIT_MERGE_VERBOSITY=5\n export GIT_MERGE_VERBOSITY\n export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME\n-- \n1.7.0.2.407.g21ebda\n"},{"id":"136515","messageId":"e69a916ca6afb53fb665951d499d7e6543007008.1268229087.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1268229087.git.trast@student.ethz.ch","subject":"[PATCH v6 02/13] Support showing notes from more than one notes tree","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-03-10T14:03:39Z","receivedAt":"2010-03-10T14:03:39Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"With this patch, you can set notes.displayRef to a glob that points at\nyour favourite notes refs, e.g.,\n\n[notes]\n\tdisplayRef = refs/notes/*\n\nThen git-log and friends will show notes from all trees.\n\nThanks to Junio C Hamano for lots of feedback, which greatly\ninfluenced the design of the entire series and this commit in\nparticular.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nChanges since v5\n* make display_notes_{refs,trees} static\n* foo() -> foo(void)\n* assert() that init_display_notes() is only called once\n* add invocation comments to the new functions in notes.h\n* bring git-notes(1) up to date wrt. notes display\n\n Documentation/config.txt         |   23 +++++-\n Documentation/git-notes.txt      |   11 ++-\n Documentation/pretty-options.txt |   11 ++-\n builtin/log.c                    |    2 +\n cache.h                          |    1 +\n notes.c                          |  173 ++++++++++++++++++++++++++++++++++++--\n notes.h                          |   57 +++++++++++++\n pretty.c                         |    6 +-\n refs.c                           |    4 +-\n refs.h                           |    5 +\n revision.c                       |   21 +++++\n revision.h                       |    5 +\n t/t3301-notes.sh                 |  148 ++++++++++++++++++++++++++++++--\n t/test-lib.sh                    |    1 +\n 14 files changed, 439 insertions(+), 29 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex d9ee519..0d0aa9c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -519,10 +519,12 @@ check that makes sure that existing object files will not get overwritten.\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+\tafter the full SHA-1 of the commit they annotate.  The ref\n+\tmust be fully qualified.\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+appended to the commit message, separated by a \"Notes (<refname>):\"\n+line (shortened to \"Notes:\" in the case of \"refs/notes/commits\").  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@@ -1341,6 +1343,23 @@ mergetool.keepTemporaries::\n mergetool.prompt::\n \tPrompt before each invocation of the merge resolution program.\n \n+notes.displayRef::\n+\tThe (fully qualified) refname from which to show notes when\n+\tshowing commit messages.  The value of this variable can be set\n+\tto a glob, in which case notes from all matching refs will be\n+\tshown.  You may also specify this configuration variable\n+\tseveral times.  A warning will be issued for refs that do not\n+\texist, but a glob that does not match any refs is silently\n+\tignored.\n++\n+This setting can be overridden with the `GIT_NOTES_DISPLAY_REF`\n+environment variable, which must be a colon separated list of refs or\n+globs.\n++\n+The effective value of \"core.notesRef\" (possibly overridden by\n+GIT_NOTES_REF) is also implicitly added to the list of refs to be\n+displayed.\n+\n pack.window::\n \tThe size of the window used by linkgit:git-pack-objects[1] when no\n \twindow size is given on the command line. Defaults to 10.\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex bef2f39..95b4bca 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -27,12 +27,13 @@ A typical use of notes is to extend a commit message without having\n to change the commit itself. Such commit notes can be shown by `git log`\n along with the original commit message. To discern these notes from the\n message stored in the commit object, the notes are indented like the\n-message, after an unindented line saying \"Notes:\".\n+message, after an unindented line saying \"Notes (<refname>):\" (or\n+\"Notes:\" for the default setting).\n \n-To disable notes, you have to set the config variable core.notesRef to\n-the empty string.  Alternatively, you can set it to a different ref,\n-something like \"refs/notes/bugzilla\".  This setting can be overridden\n-by the environment variable \"GIT_NOTES_REF\".\n+This command always manipulates the notes specified in \"core.notesRef\"\n+(see linkgit:git-config[1]), which can be overridden by GIT_NOTES_REF.\n+To change which notes are shown by 'git-log', see the\n+\"notes.displayRef\" configuration.\n \n \n SUBCOMMANDS\ndiff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt\nindex aa96cae..1d56926 100644\n--- a/Documentation/pretty-options.txt\n+++ b/Documentation/pretty-options.txt\n@@ -30,9 +30,18 @@ people using 80-column terminals.\n \tdefaults to UTF-8.\n \n --no-notes::\n---show-notes::\n+--show-notes[=<ref>]::\n \tShow the notes (see linkgit:git-notes[1]) that annotate the\n \tcommit, when showing the commit log message.  This is the default\n \tfor `git log`, `git show` and `git whatchanged` commands when\n \tthere is no `--pretty`, `--format` nor `--oneline` option is\n \tgiven on the command line.\n++\n+With an optional argument, add this ref to the list of notes.  The ref\n+is taken to be in `refs/notes/` if it is not qualified.\n+\n+--[no-]standard-notes::\n+\tEnable or disable populating the notes ref list from the\n+\t'core.notesRef' and 'notes.displayRef' variables (or\n+\tcorresponding environment overrides).  See\n+\tlinkgit:git-config[1].\ndiff --git a/builtin/log.c b/builtin/log.c\nindex b70d0f7..68d636f 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -77,6 +77,8 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n \n \tif (!rev->show_notes_given && !rev->pretty_given)\n \t\trev->show_notes = 1;\n+\tif (rev->show_notes)\n+\t\tinit_display_notes(&rev->notes_opt);\n \n \tif (rev->diffopt.pickaxe || rev->diffopt.filter)\n \t\trev->always_show_header = 0;\ndiff --git a/cache.h b/cache.h\nindex ded3090..b25d180 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -387,6 +387,7 @@ static inline enum object_type object_type(unsigned int mode)\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+#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT \"GIT_NOTES_DISPLAY_REF\"\n \n /*\n  * Repository-local GIT_* environment variables\ndiff --git a/notes.c b/notes.c\nindex a4f9926..0d4b892 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -5,6 +5,8 @@\n #include \"utf8.h\"\n #include \"strbuf.h\"\n #include \"tree-walk.h\"\n+#include \"string-list.h\"\n+#include \"refs.h\"\n \n /*\n  * Use a non-balancing simple 16-tree structure with struct int_node as\n@@ -68,6 +70,9 @@ struct non_note {\n \n struct notes_tree default_notes_tree;\n \n+static struct string_list display_notes_refs;\n+static struct notes_tree **display_notes_trees;\n+\n static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\tstruct int_node *node, unsigned int n);\n \n@@ -828,6 +833,76 @@ int combine_notes_ignore(unsigned char *cur_sha1,\n \treturn 0;\n }\n \n+static int string_list_add_one_ref(const char *path, const unsigned char *sha1,\n+\t\t\t\t   int flag, void *cb)\n+{\n+\tstruct string_list *refs = cb;\n+\tif (!unsorted_string_list_has_string(refs, path))\n+\t\tstring_list_append(path, refs);\n+\treturn 0;\n+}\n+\n+void string_list_add_refs_by_glob(struct string_list *list, const char *glob)\n+{\n+\tif (has_glob_specials(glob)) {\n+\t\tfor_each_glob_ref(string_list_add_one_ref, glob, list);\n+\t} else {\n+\t\tunsigned char sha1[20];\n+\t\tif (get_sha1(glob, sha1))\n+\t\t\twarning(\"notes ref %s is invalid\", glob);\n+\t\tif (!unsorted_string_list_has_string(list, glob))\n+\t\t\tstring_list_append(glob, list);\n+\t}\n+}\n+\n+void string_list_add_refs_from_colon_sep(struct string_list *list,\n+\t\t\t\t\t const char *globs)\n+{\n+\tstruct strbuf globbuf = STRBUF_INIT;\n+\tstruct strbuf **split;\n+\tint i;\n+\n+\tstrbuf_addstr(&globbuf, globs);\n+\tsplit = strbuf_split(&globbuf, ':');\n+\n+\tfor (i = 0; split[i]; i++) {\n+\t\tif (!split[i]->len)\n+\t\t\tcontinue;\n+\t\tif (split[i]->buf[split[i]->len-1] == ':')\n+\t\t\tstrbuf_setlen(split[i], split[i]->len-1);\n+\t\tstring_list_add_refs_by_glob(list, split[i]->buf);\n+\t}\n+\n+\tstrbuf_list_free(split);\n+\tstrbuf_release(&globbuf);\n+}\n+\n+static int notes_display_config(const char *k, const char *v, void *cb)\n+{\n+\tint *load_refs = cb;\n+\n+\tif (*load_refs && !strcmp(k, \"notes.displayref\")) {\n+\t\tif (!v)\n+\t\t\tconfig_error_nonbool(k);\n+\t\tstring_list_add_refs_by_glob(&display_notes_refs, v);\n+\t\treturn 0;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static const char *default_notes_ref(void)\n+{\n+\tconst char *notes_ref = NULL;\n+\tif (!notes_ref)\n+\t\tnotes_ref = getenv(GIT_NOTES_REF_ENVIRONMENT);\n+\tif (!notes_ref)\n+\t\tnotes_ref = notes_ref_name; /* value of core.notesRef config */\n+\tif (!notes_ref)\n+\t\tnotes_ref = GIT_NOTES_DEFAULT_REF;\n+\treturn notes_ref;\n+}\n+\n void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\tcombine_notes_fn combine_notes, int flags)\n {\n@@ -840,11 +915,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \tassert(!t->initialized);\n \n \tif (!notes_ref)\n-\t\tnotes_ref = getenv(GIT_NOTES_REF_ENVIRONMENT);\n-\tif (!notes_ref)\n-\t\tnotes_ref = notes_ref_name; /* value of core.notesRef config */\n-\tif (!notes_ref)\n-\t\tnotes_ref = GIT_NOTES_DEFAULT_REF;\n+\t\tnotes_ref = default_notes_ref();\n \n \tif (!combine_notes)\n \t\tcombine_notes = combine_notes_concatenate;\n@@ -868,6 +939,73 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \tload_subtree(t, &root_tree, t->root, 0);\n }\n \n+struct load_notes_cb_data\n+{\n+\tint counter;\n+\tstruct notes_tree **trees;\n+};\n+\n+static int load_one_display_note_ref(struct string_list_item *item,\n+\t\t\t\t     void *cb_data)\n+{\n+\tstruct load_notes_cb_data *c = cb_data;\n+\tstruct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));\n+\tinit_notes(t, item->string, combine_notes_ignore, 0);\n+\tc->trees[c->counter++] = t;\n+\treturn 0;\n+}\n+\n+struct notes_tree **load_notes_trees(struct string_list *refs)\n+{\n+\tstruct notes_tree **trees;\n+\tstruct load_notes_cb_data cb_data;\n+\ttrees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *));\n+\tcb_data.counter = 0;\n+\tcb_data.trees = trees;\n+\tfor_each_string_list(load_one_display_note_ref, refs, &cb_data);\n+\ttrees[cb_data.counter] = NULL;\n+\treturn trees;\n+}\n+\n+static int string_list_add_refs_from_list(struct string_list_item *item,\n+\t\t\t\t\t  void *cb)\n+{\n+\tstruct string_list *list = cb;\n+\tstring_list_add_refs_by_glob(list, item->string);\n+\treturn 0;\n+}\n+\n+void init_display_notes(struct display_notes_opt *opt)\n+{\n+\tchar *display_ref_env;\n+\tint load_config_refs = 0;\n+\tdisplay_notes_refs.strdup_strings = 1;\n+\n+\tassert(!display_notes_trees);\n+\n+\tif (!opt || !opt->suppress_default_notes) {\n+\t\tstring_list_append(default_notes_ref(), &display_notes_refs);\n+\t\tdisplay_ref_env = getenv(GIT_NOTES_DISPLAY_REF_ENVIRONMENT);\n+\t\tif (display_ref_env) {\n+\t\t\tstring_list_add_refs_from_colon_sep(&display_notes_refs,\n+\t\t\t\t\t\t\t    display_ref_env);\n+\t\t\tload_config_refs = 0;\n+\t\t} else {\n+\t\t\tload_config_refs = 1;\n+\t\t}\n+\t}\n+\n+\tgit_config(notes_display_config, &load_config_refs);\n+\n+\tif (opt && opt->extra_notes_refs)\n+\t\tfor_each_string_list(string_list_add_refs_from_list,\n+\t\t\t\t     opt->extra_notes_refs,\n+\t\t\t\t     &display_notes_refs);\n+\n+\tdisplay_notes_trees = load_notes_trees(&display_notes_refs);\n+\tstring_list_clear(&display_notes_refs, 0);\n+}\n+\n void add_note(struct notes_tree *t, const unsigned char *object_sha1,\n \t\tconst unsigned char *note_sha1, combine_notes_fn combine_notes)\n {\n@@ -1016,8 +1154,18 @@ void format_note(struct notes_tree *t, const unsigned char *object_sha1,\n \tif (msglen && msg[msglen - 1] == '\\n')\n \t\tmsglen--;\n \n-\tif (flags & NOTES_SHOW_HEADER)\n-\t\tstrbuf_addstr(sb, \"\\nNotes:\\n\");\n+\tif (flags & NOTES_SHOW_HEADER) {\n+\t\tconst char *ref = t->ref;\n+\t\tif (!ref || !strcmp(ref, GIT_NOTES_DEFAULT_REF)) {\n+\t\t\tstrbuf_addstr(sb, \"\\nNotes:\\n\");\n+\t\t} else {\n+\t\t\tif (!prefixcmp(ref, \"refs/\"))\n+\t\t\t\tref += 5;\n+\t\t\tif (!prefixcmp(ref, \"notes/\"))\n+\t\t\t\tref += 6;\n+\t\t\tstrbuf_addf(sb, \"\\nNotes (%s):\\n\", ref);\n+\t\t}\n+\t}\n \n \tfor (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {\n \t\tlinelen = strchrnul(msg_p, '\\n') - msg_p;\n@@ -1030,3 +1178,14 @@ void format_note(struct notes_tree *t, const unsigned char *object_sha1,\n \n \tfree(msg);\n }\n+\n+void format_display_notes(const unsigned char *object_sha1,\n+\t\t\t  struct strbuf *sb, const char *output_encoding, int flags)\n+{\n+\tint i;\n+\tif (!display_notes_trees)\n+\t\tinit_display_notes(NULL);\n+\tfor (i = 0; display_notes_trees[i]; i++)\n+\t\tformat_note(display_notes_trees[i], object_sha1, sb,\n+\t\t\t    output_encoding, flags);\n+}\ndiff --git a/notes.h b/notes.h\nindex bad03cc..db47b67 100644\n--- a/notes.h\n+++ b/notes.h\n@@ -198,4 +198,61 @@ int for_each_note(struct notes_tree *t, int flags, each_note_fn fn,\n void format_note(struct notes_tree *t, const unsigned char *object_sha1,\n \t\tstruct strbuf *sb, const char *output_encoding, int flags);\n \n+\n+struct string_list;\n+\n+struct display_notes_opt\n+{\n+\tint suppress_default_notes : 1;\n+\tstruct string_list *extra_notes_refs;\n+};\n+\n+/*\n+ * Load the notes machinery for displaying several notes trees.\n+ *\n+ * If 'opt' is not NULL, then it specifies additional settings for the\n+ * displaying:\n+ *\n+ * - suppress_default_notes indicates that the notes from\n+ *   core.notesRef and notes.displayRef should not be loaded.\n+ *\n+ * - extra_notes_refs may contain a list of globs (in the same style\n+ *   as notes.displayRef) where notes should be loaded from.\n+ */\n+void init_display_notes(struct display_notes_opt *opt);\n+\n+/*\n+ * Append notes for the given 'object_sha1' from all trees set up by\n+ * init_display_notes() to 'sb'.  The 'flags' are a bitwise\n+ * combination of\n+ *\n+ * - NOTES_SHOW_HEADER: add a 'Notes (refname):' header\n+ *\n+ * - NOTES_INDENT: indent the notes by 4 places\n+ *\n+ * init_display_notes() is called implicitly for you if you haven't\n+ * already.\n+ */\n+void format_display_notes(const unsigned char *object_sha1,\n+\t\t\t  struct strbuf *sb, const char *output_encoding, int flags);\n+\n+/*\n+ * Load the notes tree from each ref listed in 'refs'.  The output is\n+ * an array of notes_tree*, terminated by a NULL.\n+ */\n+struct notes_tree **load_notes_trees(struct string_list *refs);\n+\n+/*\n+ * Add all refs that match 'glob' to the 'list'.\n+ */\n+void string_list_add_refs_by_glob(struct string_list *list, const char *glob);\n+\n+/*\n+ * Add all refs from a colon-separated glob list 'globs' to the end of\n+ * 'list'.  Empty components are ignored.  This helper is used to\n+ * parse GIT_NOTES_DISPLAY_REF style environment variables.\n+ */\n+void string_list_add_refs_from_colon_sep(struct string_list *list,\n+\t\t\t\t\t const char *globs);\n+\n #endif\ndiff --git a/pretty.c b/pretty.c\nindex f999485..6ba3da8 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -775,7 +775,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\t}\n \t\treturn 0;\t/* unknown %g placeholder */\n \tcase 'N':\n-\t\tformat_note(NULL, commit->object.sha1, sb,\n+\t\tformat_display_notes(commit->object.sha1, sb,\n \t\t\t    git_log_output_encoding ? git_log_output_encoding\n \t\t\t\t\t\t    : git_commit_encoding, 0);\n \t\treturn 1;\n@@ -1096,8 +1096,8 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,\n \t\tstrbuf_addch(sb, '\\n');\n \n \tif (context->show_notes)\n-\t\tformat_note(NULL, commit->object.sha1, sb, encoding,\n-\t\t\t    NOTES_SHOW_HEADER | NOTES_INDENT);\n+\t\tformat_display_notes(commit->object.sha1, sb, encoding,\n+\t\t\t\t     NOTES_SHOW_HEADER | NOTES_INDENT);\n \n \tfree(reencoded);\n }\ndiff --git a/refs.c b/refs.c\nindex f3fcbe0..5a860c4 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -695,7 +695,6 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,\n {\n \tstruct strbuf real_pattern = STRBUF_INIT;\n \tstruct ref_filter filter;\n-\tconst char *has_glob_specials;\n \tint ret;\n \n \tif (!prefix && prefixcmp(pattern, \"refs/\"))\n@@ -704,8 +703,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,\n \t\tstrbuf_addstr(&real_pattern, prefix);\n \tstrbuf_addstr(&real_pattern, pattern);\n \n-\thas_glob_specials = strpbrk(pattern, \"?*[\");\n-\tif (!has_glob_specials) {\n+\tif (!has_glob_specials(pattern)) {\n \t\t/* Append implied '/' '*' if not present. */\n \t\tif (real_pattern.buf[real_pattern.len - 1] != '/')\n \t\t\tstrbuf_addch(&real_pattern, '/');\ndiff --git a/refs.h b/refs.h\nindex f7648b9..4a18b08 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -28,6 +28,11 @@ struct ref_lock {\n extern int for_each_glob_ref(each_ref_fn, const char *pattern, void *);\n extern int for_each_glob_ref_in(each_ref_fn, const char *pattern, const char* prefix, void *);\n \n+static inline const char *has_glob_specials(const char *pattern)\n+{\n+\treturn strpbrk(pattern, \"?*[\");\n+}\n+\n /* can be used to learn about broken ref and symref */\n extern int for_each_rawref(each_ref_fn, void *);\n \ndiff --git a/revision.c b/revision.c\nindex 29721ec..d6e842e 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -12,6 +12,7 @@\n #include \"patch-ids.h\"\n #include \"decorate.h\"\n #include \"log-tree.h\"\n+#include \"string-list.h\"\n \n volatile show_early_output_fn_t show_early_output;\n \n@@ -1191,9 +1192,29 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t} else if (!strcmp(arg, \"--show-notes\")) {\n \t\trevs->show_notes = 1;\n \t\trevs->show_notes_given = 1;\n+\t} else if (!prefixcmp(arg, \"--show-notes=\")) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\trevs->show_notes = 1;\n+\t\trevs->show_notes_given = 1;\n+\t\tif (!revs->notes_opt.extra_notes_refs)\n+\t\t\trevs->notes_opt.extra_notes_refs = xcalloc(1, sizeof(struct string_list));\n+\t\tif (!prefixcmp(arg+13, \"refs/\"))\n+\t\t\t/* happy */;\n+\t\telse if (!prefixcmp(arg+13, \"notes/\"))\n+\t\t\tstrbuf_addstr(&buf, \"refs/\");\n+\t\telse\n+\t\t\tstrbuf_addstr(&buf, \"refs/notes/\");\n+\t\tstrbuf_addstr(&buf, arg+13);\n+\t\tstring_list_append(strbuf_detach(&buf, NULL),\n+\t\t\t\t   revs->notes_opt.extra_notes_refs);\n \t} else if (!strcmp(arg, \"--no-notes\")) {\n \t\trevs->show_notes = 0;\n \t\trevs->show_notes_given = 1;\n+\t} else if (!strcmp(arg, \"--standard-notes\")) {\n+\t\trevs->show_notes_given = 1;\n+\t\trevs->notes_opt.suppress_default_notes = 0;\n+\t} else if (!strcmp(arg, \"--no-standard-notes\")) {\n+\t\trevs->notes_opt.suppress_default_notes = 1;\n \t} else if (!strcmp(arg, \"--oneline\")) {\n \t\trevs->verbose_header = 1;\n \t\tget_commit_format(\"oneline\", revs);\ndiff --git a/revision.h b/revision.h\nindex a14deef..580f6ec 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -3,6 +3,7 @@\n \n #include \"parse-options.h\"\n #include \"grep.h\"\n+#include \"notes.h\"\n \n #define SEEN\t\t(1u<<0)\n #define UNINTERESTING   (1u<<1)\n@@ -20,6 +21,7 @@\n \n struct rev_info;\n struct log_info;\n+struct string_list;\n \n struct rev_info {\n \t/* Starting list */\n@@ -126,6 +128,9 @@ struct rev_info {\n \tstruct reflog_walk_info *reflog_info;\n \tstruct decoration children;\n \tstruct decoration merge_simplification;\n+\n+\t/* notes-specific options: which refs to show */\n+\tstruct display_notes_opt notes_opt;\n };\n \n #define REV_TREE_SAME\t\t0\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex 37b9687..f6cdb33 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -416,7 +416,7 @@ Date:   Thu Apr 7 15:18:13 2005 -0700\n \n     6th\n \n-Notes:\n+Notes (other):\n     other note\n EOF\n \n@@ -449,7 +449,139 @@ test_expect_success 'Do not show note when core.notesRef is overridden' '\n \ttest_cmp expect-not-other output\n '\n \n+cat > expect-both << EOF\n+commit 387a89921c73d7ed72cd94d179c1c7048ca47756\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:18:13 2005 -0700\n+\n+    6th\n+\n+Notes:\n+    order test\n+\n+Notes (other):\n+    other note\n+\n+commit bd1753200303d0a0344be813e504253b3d98e74d\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:17:13 2005 -0700\n+\n+    5th\n+\n+Notes:\n+    replacement for deleted note\n+EOF\n+\n+test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '\n+\tGIT_NOTES_REF=refs/notes/commits git notes add \\\n+\t\t-m\"replacement for deleted note\" HEAD^ &&\n+\tGIT_NOTES_REF=refs/notes/commits git notes add -m\"order test\" &&\n+\tgit config --unset core.notesRef &&\n+\tgit config notes.displayRef \"refs/notes/*\" &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect-both output\n+'\n+\n+test_expect_success 'core.notesRef is implicitly in notes.displayRef' '\n+\tgit config core.notesRef refs/notes/commits &&\n+\tgit config notes.displayRef refs/notes/other &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect-both output\n+'\n+\n+test_expect_success 'notes.displayRef can be given more than once' '\n+\tgit config --unset core.notesRef &&\n+\tgit config notes.displayRef refs/notes/commits &&\n+\tgit config --add notes.displayRef refs/notes/other &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect-both output\n+'\n+\n+cat > expect-both-reversed << EOF\n+commit 387a89921c73d7ed72cd94d179c1c7048ca47756\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:18:13 2005 -0700\n+\n+    6th\n+\n+Notes (other):\n+    other note\n+\n+Notes:\n+    order test\n+EOF\n+\n+test_expect_success 'notes.displayRef respects order' '\n+\tgit config core.notesRef refs/notes/other &&\n+\tgit config --unset-all notes.displayRef &&\n+\tgit config notes.displayRef refs/notes/commits &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect-both-reversed output\n+'\n+\n+test_expect_success 'GIT_NOTES_DISPLAY_REF works' '\n+\tgit config --unset-all core.notesRef &&\n+\tgit config --unset-all notes.displayRef &&\n+\tGIT_NOTES_DISPLAY_REF=refs/notes/commits:refs/notes/other \\\n+\t\tgit log -2 > output &&\n+\ttest_cmp expect-both output\n+'\n+\n+cat > expect-none << EOF\n+commit 387a89921c73d7ed72cd94d179c1c7048ca47756\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:18:13 2005 -0700\n+\n+    6th\n+\n+commit bd1753200303d0a0344be813e504253b3d98e74d\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:17:13 2005 -0700\n+\n+    5th\n+EOF\n+\n+test_expect_success 'GIT_NOTES_DISPLAY_REF overrides config' '\n+\tgit config notes.displayRef \"refs/notes/*\" &&\n+\tGIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log -2 > output &&\n+\ttest_cmp expect-none output\n+'\n+\n+test_expect_success '--show-notes=* adds to GIT_NOTES_DISPLAY_REF' '\n+\tGIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log --show-notes=* -2 > output &&\n+\ttest_cmp expect-both output\n+'\n+\n+cat > expect-commits << EOF\n+commit 387a89921c73d7ed72cd94d179c1c7048ca47756\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:18:13 2005 -0700\n+\n+    6th\n+\n+Notes:\n+    order test\n+EOF\n+\n+test_expect_success '--no-standard-notes' '\n+\tgit log --no-standard-notes --show-notes=commits -1 > output &&\n+\ttest_cmp expect-commits output\n+'\n+\n+test_expect_success '--standard-notes' '\n+\tgit log --no-standard-notes --show-notes=commits \\\n+\t\t--standard-notes -2 > output &&\n+\ttest_cmp expect-both output\n+'\n+\n+test_expect_success '--show-notes=ref accumulates' '\n+\tgit log --show-notes=other --show-notes=commits \\\n+\t\t --no-standard-notes -1 > output &&\n+\ttest_cmp expect-both-reversed output\n+'\n+\n test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '\n+\tgit config core.notesRef refs/notes/other &&\n \techo \"Note on a tree\" > expect\n \tgit notes add -m \"Note on a tree\" HEAD: &&\n \tgit notes show HEAD: > actual &&\n@@ -473,7 +605,7 @@ Date:   Thu Apr 7 15:19:13 2005 -0700\n \n     7th\n \n-Notes:\n+Notes (other):\n     other note\n EOF\n \n@@ -504,7 +636,7 @@ Date:   Thu Apr 7 15:21:13 2005 -0700\n \n     9th\n \n-Notes:\n+Notes (other):\n     yet another note\n EOF\n \n@@ -534,7 +666,7 @@ Date:   Thu Apr 7 15:21:13 2005 -0700\n \n     9th\n \n-Notes:\n+Notes (other):\n     yet another note\n $whitespace\n     yet another note\n@@ -553,7 +685,7 @@ Date:   Thu Apr 7 15:22:13 2005 -0700\n \n     10th\n \n-Notes:\n+Notes (other):\n     other note\n EOF\n \n@@ -570,7 +702,7 @@ Date:   Thu Apr 7 15:22:13 2005 -0700\n \n     10th\n \n-Notes:\n+Notes (other):\n     other note\n $whitespace\n     yet another note\n@@ -589,7 +721,7 @@ Date:   Thu Apr 7 15:23:13 2005 -0700\n \n     11th\n \n-Notes:\n+Notes (other):\n     other note\n $whitespace\n     yet another note\n@@ -620,7 +752,7 @@ Date:   Thu Apr 7 15:23:13 2005 -0700\n \n     11th\n \n-Notes:\n+Notes (other):\n     yet another note\n $whitespace\n     yet another note\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 400bc6b..3d026b4 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -55,6 +55,7 @@ unset GIT_CEILING_DIRECTORIES\n unset SHA1_FILE_DIRECTORIES\n unset SHA1_FILE_DIRECTORY\n unset GIT_NOTES_REF\n+unset GIT_NOTES_DISPLAY_REF\n GIT_MERGE_VERBOSITY=5\n export GIT_MERGE_VERBOSITY\n export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME\n-- \n1.7.0.2.407.g21ebda\n"},{"id":"136514","messageId":"0aef1241fac194157f246f3864517b6a318340a4.1268229087.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1268229087.git.trast@student.ethz.ch","subject":"[PATCH v6 03/13] Documentation: document post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-03-10T14:03:40Z","receivedAt":"2010-03-10T14:03:40Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"This defines the behaviour of the post-rewrite hook support, which\nwill be implemented in the following patches.\n\nWe deliberately do not document how often the hook will be invoked per\nrewriting command, but the interface is designed to keep that at\n\"once\".  This would currently not matter too much, since both rebase\nand filter-branch are shellscripts and spawn many processes anyway.\nHowever, when a fast sequencer in C is implemented, it will be\nbeneficial to only have to run the hook once.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\n\n Documentation/githooks.txt |   34 ++++++++++++++++++++++++++++++++++\n 1 files changed, 34 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex 87e2c03..a741769 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -317,6 +317,40 @@ This hook is invoked by 'git gc --auto'. It takes no parameter, and\n exiting with non-zero status from this script causes the 'git gc --auto'\n to abort.\n \n+post-rewrite\n+~~~~~~~~~~~~\n+\n+This hook is invoked by commands that rewrite commits (`git commit\n+--amend`, 'git-rebase'; currently 'git-filter-branch' does 'not' call\n+it!).  Its first argument denotes the command it was invoked by:\n+currently one of `amend` or `rebase`.  Further command-dependent\n+arguments may be passed in the future.\n+\n+The hook receives a list of the rewritten commits on stdin, in the\n+format\n+\n+  <old-sha1> SP <new-sha1> [ SP <extra-info> ] LF\n+\n+The 'extra-info' is again command-dependent.  If it is empty, the\n+preceding SP is also omitted.  Currently, no commands pass any\n+'extra-info'.\n+\n+The following command-specific comments apply:\n+\n+rebase::\n+\tFor the 'squash' and 'fixup' operation, all commits that were\n+\tsquashed are listed as being rewritten to the squashed commit.\n+\tThis means that there will be several lines sharing the same\n+\t'new-sha1'.\n++\n+The commits are guaranteed to be listed in the order that they were\n+processed by rebase.\n+\n+There is no default 'post-rewrite' hook, but see the\n+`post-receive-copy-notes` script in `contrib/hooks` for an example\n+that copies your git-notes to the rewritten commits.\n+\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \n1.7.0.2.407.g21ebda\n"},{"id":"136516","messageId":"7a9d9199bc40db87502ca6bfc75376fef8b73b12.1268229087.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1268229087.git.trast@student.ethz.ch","subject":"[PATCH v6 04/13] commit --amend: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-03-10T14:05:51Z","receivedAt":"2010-03-10T14:05:51Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"The rough structure of run_rewrite_hook() comes from\nrun_receive_hook() in receive-pack.\n\nWe introduce a --no-post-rewrite option and use it to avoid the hook\nwhen called from git-rebase -i 'edit'.  The next patch will add full\nsupport in git-rebase, and we only want to invoke the hook once.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\n\n builtin/commit.c             |   39 +++++++++++++++++++++++++++++++\n git-rebase--interactive.sh   |    2 +-\n t/t5407-post-rewrite-hook.sh |   52 ++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 92 insertions(+), 1 deletions(-)\n create mode 100755 t/t5407-post-rewrite-hook.sh\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex f4c7344..0367412 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -66,6 +66,7 @@\n static char *author_name, *author_email, *author_date;\n static int all, edit_flag, also, interactive, only, amend, signoff;\n static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;\n+static int no_post_rewrite;\n static char *untracked_files_arg, *force_date;\n /*\n  * The default commit message cleanup mode will remove the lines\n@@ -137,6 +138,7 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset)\n \tOPT_BOOLEAN('z', \"null\", &null_termination,\n \t\t    \"terminate entries with NUL\"),\n \tOPT_BOOLEAN(0, \"amend\", &amend, \"amend previous commit\"),\n+\tOPT_BOOLEAN(0, \"no-post-rewrite\", &no_post_rewrite, \"bypass post-rewrite hook\"),\n \t{ OPTION_STRING, 'u', \"untracked-files\", &untracked_files_arg, \"mode\", \"show untracked files, optional modes: all, normal, no. (Default: all)\", PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n \tOPT_BOOLEAN(0, \"allow-empty\", &allow_empty, \"ok to record an empty change\"),\n \t/* end commit contents options */\n@@ -1160,6 +1162,40 @@ static int git_commit_config(const char *k, const char *v, void *cb)\n \treturn git_status_config(k, v, s);\n }\n \n+static const char post_rewrite_hook[] = \"hooks/post-rewrite\";\n+\n+static int run_rewrite_hook(const unsigned char *oldsha1,\n+\t\t\t    const unsigned char *newsha1)\n+{\n+\t/* oldsha1 SP newsha1 LF NUL */\n+\tstatic char buf[2*40 + 3];\n+\tstruct child_process proc;\n+\tconst char *argv[3];\n+\tint code;\n+\tsize_t n;\n+\n+\tif (access(git_path(post_rewrite_hook), X_OK) < 0)\n+\t\treturn 0;\n+\n+\targv[0] = git_path(post_rewrite_hook);\n+\targv[1] = \"amend\";\n+\targv[2] = NULL;\n+\n+\tmemset(&proc, 0, sizeof(proc));\n+\tproc.argv = argv;\n+\tproc.in = -1;\n+\tproc.stdout_to_stderr = 1;\n+\n+\tcode = start_command(&proc);\n+\tif (code)\n+\t\treturn code;\n+\tn = snprintf(buf, sizeof(buf), \"%s %s\\n\",\n+\t\t     sha1_to_hex(oldsha1), sha1_to_hex(newsha1));\n+\twrite_in_full(proc.in, buf, n);\n+\tclose(proc.in);\n+\treturn finish_command(&proc);\n+}\n+\n int cmd_commit(int argc, const char **argv, const char *prefix)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -1303,6 +1339,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \n \trerere(0);\n \trun_hook(get_index_file(), \"post-commit\", NULL);\n+\tif (amend && !no_post_rewrite) {\n+\t\trun_rewrite_hook(head_sha1, commit_sha1);\n+\t}\n \tif (!quiet)\n \t\tprint_summary(prefix, commit_sha1);\n \ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex 92d19f5..c38efc4 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -436,7 +436,7 @@ do_next () {\n \t\tmark_action_done\n \t\tpick_one $sha1 ||\n \t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n-\t\tgit commit --amend\n+\t\tgit commit --amend --no-post-rewrite\n \t\t;;\n \tedit|e)\n \t\tcomment_for_reflog edit\ndiff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh\nnew file mode 100755\nindex 0000000..1020af9\n--- /dev/null\n+++ b/t/t5407-post-rewrite-hook.sh\n@@ -0,0 +1,52 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2010 Thomas Rast\n+#\n+\n+test_description='Test the post-rewrite hook.'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit A foo A &&\n+\ttest_commit B foo B &&\n+\ttest_commit C foo C &&\n+\ttest_commit D foo D\n+'\n+\n+mkdir .git/hooks\n+\n+cat >.git/hooks/post-rewrite <<EOF\n+#!/bin/sh\n+echo \\$@ > \"$TRASH_DIRECTORY\"/post-rewrite.args\n+cat > \"$TRASH_DIRECTORY\"/post-rewrite.data\n+EOF\n+chmod u+x .git/hooks/post-rewrite\n+\n+clear_hook_input () {\n+\trm -f post-rewrite.args post-rewrite.data\n+}\n+\n+verify_hook_input () {\n+\ttest_cmp \"$TRASH_DIRECTORY\"/post-rewrite.args expected.args &&\n+\ttest_cmp \"$TRASH_DIRECTORY\"/post-rewrite.data expected.data\n+}\n+\n+test_expect_success 'git commit --amend' '\n+\tclear_hook_input &&\n+\techo \"D new message\" > newmsg &&\n+\toldsha=$(git rev-parse HEAD^0) &&\n+\tgit commit -Fnewmsg --amend &&\n+\techo amend > expected.args &&\n+\techo $oldsha $(git rev-parse HEAD^0) > expected.data &&\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git commit --amend --no-post-rewrite' '\n+\tclear_hook_input &&\n+\techo \"D new message again\" > newmsg &&\n+\tgit commit --no-post-rewrite -Fnewmsg --amend &&\n+\ttest ! -f post-rewrite.args &&\n+\ttest ! -f post-rewrite.data\n+'\n+\n+test_done\n-- \n1.7.0.2.407.g21ebda\n"},{"id":"136525","messageId":"f0eb9f46d10ccbc3926666fd613bcdc5c7ca9b3c.1268229087.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1268229087.git.trast@student.ethz.ch","subject":"[PATCH v6 05/13] rebase: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-03-10T14:05:52Z","receivedAt":"2010-03-10T14:05:52Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"We have to deal with two separate code paths: a normal rebase, which\nactually goes through git-am; and rebase {-m|-s}.\n\nThe only small issue with both is that they need to remember the\noriginal sha1 across a possible conflict resolution.  rebase -m\nalready puts this information in $dotest/current, and we just\nintroduce a similar file for git-am.\n\nNote that in git-am, the hook really only runs when coming from\ngit-rebase: the code path that sets the $dotest/original-commit file\nis guarded by a test for $dotest/rebasing.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\n\n git-am.sh                    |   10 ++++++++++\n git-rebase.sh                |    5 +++++\n t/t5407-post-rewrite-hook.sh |   30 ++++++++++++++++++++++++++++++\n 3 files changed, 45 insertions(+), 0 deletions(-)\n\ndiff --git a/git-am.sh b/git-am.sh\nindex 50a292a..9b73e0b 100755\n--- a/git-am.sh\n+++ b/git-am.sh\n@@ -593,6 +593,7 @@ do\n \t\t\techo \"Patch is empty.  Was it split wrong?\"\n \t\t\tstop_here $this\n \t\t}\n+\t\trm -f \"$dotest/original-commit\"\n \t\tif test -f \"$dotest/rebasing\" &&\n \t\t\tcommit=$(sed -e 's/^From \\([0-9a-f]*\\) .*/\\1/' \\\n \t\t\t\t-e q \"$dotest/$msgnum\") &&\n@@ -600,6 +601,7 @@ do\n \t\tthen\n \t\t\tgit cat-file commit \"$commit\" |\n \t\t\tsed -e '1,/^$/d' >\"$dotest/msg-clean\"\n+\t\t\techo \"$commit\" > \"$dotest/original-commit\"\n \t\telse\n \t\t\t{\n \t\t\t\tsed -n '/^Subject/ s/Subject: //p' \"$dotest/info\"\n@@ -783,6 +785,10 @@ do\n \tgit update-ref -m \"$GIT_REFLOG_ACTION: $FIRSTLINE\" HEAD $commit $parent ||\n \tstop_here $this\n \n+\tif test -f \"$dotest/original-commit\"; then\n+\t\techo \"$(cat \"$dotest/original-commit\") $commit\" >> \"$dotest/rewritten\"\n+\tfi\n+\n \tif test -x \"$GIT_DIR\"/hooks/post-applypatch\n \tthen\n \t\t\"$GIT_DIR\"/hooks/post-applypatch\n@@ -791,5 +797,9 @@ do\n \tgo_next\n done\n \n+if test -s \"$dotest\"/rewritten && test -x \"$GIT_DIR\"/hooks/post-rewrite; then\n+\t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$dotest\"/rewritten\n+fi\n+\n rm -fr \"$dotest\"\n git gc --auto\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex fb4fef7..52f8b9b 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -79,6 +79,7 @@ continue_merge () {\n \t\tthen\n \t\t\tprintf \"Committed: %0${prec}d \" $msgnum\n \t\tfi\n+\t\techo \"$cmt $(git rev-parse HEAD^0)\" >> \"$dotest/rewritten\"\n \telse\n \t\tif test -z \"$GIT_QUIET\"\n \t\tthen\n@@ -151,6 +152,10 @@ move_to_original_branch () {\n \n finish_rb_merge () {\n \tmove_to_original_branch\n+\tif test -x \"$GIT_DIR\"/hooks/post-rewrite &&\n+\t\ttest -s \"$dotest\"/rewritten; then\n+\t\t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$dotest\"/rewritten\n+\tfi\n \trm -r \"$dotest\"\n \tsay All done.\n }\ndiff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh\nindex 1020af9..1ecaa4b 100755\n--- a/t/t5407-post-rewrite-hook.sh\n+++ b/t/t5407-post-rewrite-hook.sh\n@@ -49,4 +49,34 @@ test_expect_success 'git commit --amend --no-post-rewrite' '\n \ttest ! -f post-rewrite.data\n '\n \n+test_expect_success 'git rebase' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\ttest_must_fail git rebase --onto A B &&\n+\techo C > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse C) $(git rev-parse HEAD^)\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git rebase --skip' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\ttest_must_fail git rebase --onto A B &&\n+\ttest_must_fail git rebase --skip &&\n+\techo D > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n test_done\n-- \n1.7.0.2.407.g21ebda\n"},{"id":"136517","messageId":"9123a6caf2b8a320da125eb4fcefcd6f84234664.1268229087.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1268229087.git.trast@student.ethz.ch","subject":"[PATCH v6 06/13] rebase -i: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-03-10T14:05:53Z","receivedAt":"2010-03-10T14:05:53Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Aside from the same issue that rebase also has (remembering the\noriginal commit across a conflict resolution), rebase -i brings an\nextra twist: We need to defer writing the rewritten list in the case\nof {squash,fixup} because their rewritten result should be the last\ncommit in the squashed group.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nChanges since v5\n* work around one-shot export to test_must_fail\n\n git-rebase--interactive.sh   |   46 +++++++++++++++++++-\n t/t5407-post-rewrite-hook.sh |  101 ++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 146 insertions(+), 1 deletions(-)\n\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex c38efc4..f18a118 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -96,6 +96,13 @@ AUTHOR_SCRIPT=\"$DOTEST\"/author-script\n # command is processed, this file is deleted.\n AMEND=\"$DOTEST\"/amend\n \n+# For the post-rewrite hook, we make a list of rewritten commits and\n+# their new sha1s.  The rewritten-pending list keeps the sha1s of\n+# commits that have been processed, but not committed yet,\n+# e.g. because they are waiting for a 'squash' command.\n+REWRITTEN_LIST=\"$DOTEST\"/rewritten-list\n+REWRITTEN_PENDING=\"$DOTEST\"/rewritten-pending\n+\n PRESERVE_MERGES=\n STRATEGY=\n ONTO=\n@@ -198,6 +205,7 @@ make_patch () {\n }\n \n die_with_patch () {\n+\techo \"$1\" > \"$DOTEST\"/stopped-sha\n \tmake_patch \"$1\"\n \tgit rerere\n \tdie \"$2\"\n@@ -339,6 +347,7 @@ pick_one_preserving_merges () {\n \t\t\t\tprintf \"%s\\n\" \"$msg\" > \"$GIT_DIR\"/MERGE_MSG\n \t\t\t\tdie_with_patch $sha1 \"Error redoing merge $sha1\"\n \t\t\tfi\n+\t\t\techo \"$sha1 $(git rev-parse HEAD^0)\" >> \"$REWRITTEN_LIST\"\n \t\t\t;;\n \t\t*)\n \t\t\toutput git cherry-pick \"$@\" ||\n@@ -416,6 +425,26 @@ die_failed_squash() {\n \tdie_with_patch $1 \"\"\n }\n \n+flush_rewritten_pending() {\n+\ttest -s \"$REWRITTEN_PENDING\" || return\n+\tnewsha1=\"$(git rev-parse HEAD^0)\"\n+\tsed \"s/$/ $newsha1/\" < \"$REWRITTEN_PENDING\" >> \"$REWRITTEN_LIST\"\n+\trm -f \"$REWRITTEN_PENDING\"\n+}\n+\n+record_in_rewritten() {\n+\toldsha1=\"$(git rev-parse $1)\"\n+\techo \"$oldsha1\" >> \"$REWRITTEN_PENDING\"\n+\n+\tcase \"$(peek_next_command)\" in\n+\t    squash|s|fixup|f)\n+\t\t;;\n+\t    *)\n+\t\tflush_rewritten_pending\n+\t\t;;\n+\tesac\n+}\n+\n do_next () {\n \trm -f \"$MSG\" \"$AUTHOR_SCRIPT\" \"$AMEND\" || exit\n \tread command sha1 rest < \"$TODO\"\n@@ -429,6 +458,7 @@ do_next () {\n \t\tmark_action_done\n \t\tpick_one $sha1 ||\n \t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n+\t\trecord_in_rewritten $sha1\n \t\t;;\n \treword|r)\n \t\tcomment_for_reflog reword\n@@ -437,6 +467,7 @@ do_next () {\n \t\tpick_one $sha1 ||\n \t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n \t\tgit commit --amend --no-post-rewrite\n+\t\trecord_in_rewritten $sha1\n \t\t;;\n \tedit|e)\n \t\tcomment_for_reflog edit\n@@ -444,6 +475,7 @@ do_next () {\n \t\tmark_action_done\n \t\tpick_one $sha1 ||\n \t\t\tdie_with_patch $sha1 \"Could not apply $sha1... $rest\"\n+\t\techo \"$1\" > \"$DOTEST\"/stopped-sha\n \t\tmake_patch $sha1\n \t\tgit rev-parse --verify HEAD > \"$AMEND\"\n \t\twarn \"Stopped at $sha1... $rest\"\n@@ -500,6 +532,7 @@ do_next () {\n \t\t\trm -f \"$SQUASH_MSG\" \"$FIXUP_MSG\"\n \t\t\t;;\n \t\tesac\n+\t\trecord_in_rewritten $sha1\n \t\t;;\n \t*)\n \t\twarn \"Unknown command: $command $sha1 $rest\"\n@@ -528,6 +561,11 @@ do_next () {\n \t\ttest ! -f \"$DOTEST\"/verbose ||\n \t\t\tgit diff-tree --stat $(cat \"$DOTEST\"/head)..HEAD\n \t} &&\n+\tif test -x \"$GIT_DIR\"/hooks/post-rewrite &&\n+\t\ttest -s \"$REWRITTEN_LIST\"; then\n+\t\t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$REWRITTEN_LIST\"\n+\t\ttrue # we don't care if this hook failed\n+\tfi &&\n \trm -rf \"$DOTEST\" &&\n \tgit gc --auto &&\n \twarn \"Successfully rebased and updated $HEADNAME.\"\n@@ -562,7 +600,12 @@ skip_unnecessary_picks () {\n \t\tesac\n \t\techo \"$command${sha1:+ }$sha1${rest:+ }$rest\" >&$fd\n \tdone <\"$TODO\" >\"$TODO.new\" 3>>\"$DONE\" &&\n-\tmv -f \"$TODO\".new \"$TODO\" ||\n+\tmv -f \"$TODO\".new \"$TODO\" &&\n+\tcase \"$(peek_next_command)\" in\n+\tsquash|s|fixup|f)\n+\t\trecord_in_rewritten \"$ONTO\"\n+\t\t;;\n+\tesac ||\n \tdie \"Could not skip unnecessary pick commands\"\n }\n \n@@ -676,6 +719,7 @@ first and then run 'git rebase --continue' again.\"\n \t\t\t\ttest -n \"$amend\" && git reset --soft $amend\n \t\t\t\tdie \"Could not commit staged changes.\"\n \t\t\t}\n+\t\t\trecord_in_rewritten \"$(cat \"$DOTEST\"/stopped-sha)\"\n \t\tfi\n \n \t\trequire_clean_work_tree\ndiff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh\nindex 1ecaa4b..f0f91f1 100755\n--- a/t/t5407-post-rewrite-hook.sh\n+++ b/t/t5407-post-rewrite-hook.sh\n@@ -79,4 +79,105 @@ EOF\n \tverify_hook_input\n '\n \n+test_expect_success 'git rebase -m' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\ttest_must_fail git rebase -m --onto A B &&\n+\techo C > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse C) $(git rev-parse HEAD^)\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git rebase -m --skip' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\ttest_must_fail git rebase --onto A B &&\n+\ttest_must_fail git rebase --skip &&\n+\techo D > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+. \"$TEST_DIRECTORY\"/lib-rebase.sh\n+\n+set_fake_editor\n+\n+# Helper to work around the lack of one-shot exporting for\n+# test_must_fail (as it is a shell function)\n+test_fail_interactive_rebase () {\n+\t(\n+\t\tFAKE_LINES=\"$1\" &&\n+\t\tshift &&\n+\t\texport FAKE_LINES &&\n+\t\ttest_must_fail git rebase -i \"$@\"\n+\t)\n+}\n+\n+test_expect_success 'git rebase -i (unchanged)' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\ttest_fail_interactive_rebase \"1 2\" --onto A B &&\n+\techo C > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse C) $(git rev-parse HEAD^)\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git rebase -i (skip)' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\ttest_fail_interactive_rebase \"2\" --onto A B &&\n+\techo D > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git rebase -i (squash)' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\ttest_fail_interactive_rebase \"1 squash 2\" --onto A B &&\n+\techo C > foo &&\n+\tgit add foo &&\n+\tgit rebase --continue &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse C) $(git rev-parse HEAD)\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n+test_expect_success 'git rebase -i (fixup without conflict)' '\n+\tgit reset --hard D &&\n+\tclear_hook_input &&\n+\tFAKE_LINES=\"1 fixup 2\" git rebase -i B &&\n+\techo rebase >expected.args &&\n+\tcat >expected.data <<EOF &&\n+$(git rev-parse C) $(git rev-parse HEAD)\n+$(git rev-parse D) $(git rev-parse HEAD)\n+EOF\n+\tverify_hook_input\n+'\n+\n test_done\n-- \n1.7.0.2.407.g21ebda\n"},{"id":"136523","messageId":"a6795d8aa8dd21236cca0fc61e55d3d306465e62.1268229087.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1268229087.git.trast@student.ethz.ch","subject":"[PATCH v6 07/13] notes: implement 'git notes copy --stdin'","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-03-10T14:05:54Z","receivedAt":"2010-03-10T14:05:54Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"This implements a mass-copy command that takes a sequence of lines in\nthe format\n\n  <from-sha1> SP <to-sha1> [ SP <rest> ] LF\n\non stdin, and copies each <from-sha1>'s notes to the <to-sha1>.  The\n<rest> is ignored.  The intent, of course, is that this can read the\nsame input that the 'post-rewrite' hook gets.\n\nThe copy_note() function is exposed for everyone's and in particular\nthe next commit's use.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nChanges since v5\n* don't remove note we know doesn't exist\n\n Documentation/git-notes.txt |   12 ++++++++-\n builtin/notes.c             |   56 ++++++++++++++++++++++++++++++++++++++++++-\n notes.c                     |   19 ++++++++++++++\n notes.h                     |    9 +++++++\n t/t3301-notes.sh            |   34 ++++++++++++++++++++++++++\n 5 files changed, 128 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex 95b4bca..064758b 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n [verse]\n 'git notes' [list [<object>]]\n 'git notes' add [-f] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]\n-'git notes' copy [-f] <from-object> <to-object>\n+'git notes' copy [-f] ( --stdin | <from-object> <to-object> )\n 'git notes' append [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]\n 'git notes' edit [<object>]\n 'git notes' show [<object>]\n@@ -56,6 +56,16 @@ copy::\n \tobject has none (use -f to overwrite existing notes to the\n \tsecond object). This subcommand is equivalent to:\n \t`git notes add [-f] -C $(git notes list <from-object>) <to-object>`\n++\n+In `\\--stdin` mode, take lines in the format\n++\n+----------\n+<from-object> SP <to-object> [ SP <rest> ] LF\n+----------\n++\n+on standard input, and copy the notes from each <from-object> to its\n+corresponding <to-object>.  (The optional `<rest>` is ignored so that\n+the command can read the input given to the `post-rewrite` hook.)\n \n append::\n \tAppend to the notes of an existing object (defaults to HEAD).\ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex feb710a..576a989 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -269,6 +269,46 @@ int commit_notes(struct notes_tree *t, const char *msg)\n \treturn 0;\n }\n \n+int notes_copy_from_stdin(int force)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct notes_tree *t;\n+\tint ret = 0;\n+\n+\tinit_notes(NULL, NULL, NULL, 0);\n+\tt = &default_notes_tree;\n+\n+\twhile (strbuf_getline(&buf, stdin, '\\n') != EOF) {\n+\t\tunsigned char from_obj[20], to_obj[20];\n+\t\tstruct strbuf **split;\n+\t\tint err;\n+\n+\t\tsplit = strbuf_split(&buf, ' ');\n+\t\tif (!split[0] || !split[1])\n+\t\t\tdie(\"Malformed input line: '%s'.\", buf.buf);\n+\t\tstrbuf_rtrim(split[0]);\n+\t\tstrbuf_rtrim(split[1]);\n+\t\tif (get_sha1(split[0]->buf, from_obj))\n+\t\t\tdie(\"Failed to resolve '%s' as a valid ref.\", split[0]->buf);\n+\t\tif (get_sha1(split[1]->buf, to_obj))\n+\t\t\tdie(\"Failed to resolve '%s' as a valid ref.\", split[1]->buf);\n+\n+\t\terr = copy_note(t, from_obj, to_obj, force, combine_notes_overwrite);\n+\n+\t\tif (err) {\n+\t\t\terror(\"Failed to copy notes from '%s' to '%s'\",\n+\t\t\t      split[0]->buf, split[1]->buf);\n+\t\t\tret = 1;\n+\t\t}\n+\n+\t\tstrbuf_list_free(split);\n+\t}\n+\n+\tcommit_notes(t, \"Notes added by 'git notes copy'\");\n+\tfree_notes(t);\n+\treturn ret;\n+}\n+\n int cmd_notes(int argc, const char **argv, const char *prefix)\n {\n \tstruct notes_tree *t;\n@@ -278,7 +318,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \tchar logmsg[100];\n \n \tint list = 0, add = 0, copy = 0, append = 0, edit = 0, show = 0,\n-\t    remove = 0, prune = 0, force = 0;\n+\t    remove = 0, prune = 0, force = 0, from_stdin = 0;\n \tint given_object = 0, i = 1, retval = 0;\n \tstruct msg_arg msg = { 0, 0, STRBUF_INIT };\n \tstruct option options[] = {\n@@ -297,6 +337,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\t\tparse_reuse_arg},\n \t\tOPT_GROUP(\"Other options\"),\n \t\tOPT_BOOLEAN('f', \"force\", &force, \"replace existing notes\"),\n+\t\tOPT_BOOLEAN(0, \"stdin\", &from_stdin, \"read objects from stdin\"),\n \t\tOPT_END()\n \t};\n \n@@ -345,8 +386,21 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(git_notes_usage, options);\n \t}\n \n+\tif (!copy && from_stdin) {\n+\t\terror(\"cannot use --stdin with %s subcommand.\", argv[0]);\n+\t\tusage_with_options(git_notes_usage, options);\n+\t}\n+\n \tif (copy) {\n \t\tconst char *from_ref;\n+\t\tif (from_stdin) {\n+\t\t\tif (argc > 1) {\n+\t\t\t\terror(\"too many parameters\");\n+\t\t\t\tusage_with_options(git_notes_usage, options);\n+\t\t\t} else {\n+\t\t\t\treturn notes_copy_from_stdin(force);\n+\t\t\t}\n+\t\t}\n \t\tif (argc < 3) {\n \t\t\terror(\"too few parameters\");\n \t\t\tusage_with_options(git_notes_usage, options);\ndiff --git a/notes.c b/notes.c\nindex 0d4b892..b1b15e9 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -1189,3 +1189,22 @@ void format_display_notes(const unsigned char *object_sha1,\n \t\tformat_note(display_notes_trees[i], object_sha1, sb,\n \t\t\t    output_encoding, flags);\n }\n+\n+int copy_note(struct notes_tree *t,\n+\t      const unsigned char *from_obj, const unsigned char *to_obj,\n+\t      int force, combine_notes_fn combine_fn)\n+{\n+\tconst unsigned char *note = get_note(t, from_obj);\n+\tconst unsigned char *existing_note = get_note(t, to_obj);\n+\n+\tif (!force && existing_note)\n+\t\treturn 1;\n+\n+\tif (note)\n+\t\tadd_note(t, to_obj, note, combine_fn);\n+\telse if (existing_note) {\n+\t\tadd_note(t, to_obj, null_sha1, combine_fn);\n+\t}\n+\n+\treturn 0;\n+}\ndiff --git a/notes.h b/notes.h\nindex db47b67..e1868d8 100644\n--- a/notes.h\n+++ b/notes.h\n@@ -100,6 +100,15 @@ void add_note(struct notes_tree *t, const unsigned char *object_sha1,\n \t\tconst unsigned char *object_sha1);\n \n /*\n+ * Copy a note from one object to another in the given notes_tree.\n+ *\n+ * Fails if the to_obj already has a note unless 'force' is true.\n+ */\n+int copy_note(struct notes_tree *t,\n+\t      const unsigned char *from_obj, const unsigned char *to_obj,\n+\t      int force, combine_notes_fn combine_fn);\n+\n+/*\n  * Flags controlling behaviour of for_each_note()\n  *\n  * Default behaviour of for_each_note() is to traverse every single note object\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex f6cdb33..29ef0c6 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -777,4 +777,38 @@ test_expect_success 'cannot copy note from object without notes' '\n \ttest_must_fail git notes copy HEAD^ HEAD\n '\n \n+cat > expect << EOF\n+commit e5d4fb5698d564ab8c73551538ecaf2b0c666185\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:25:13 2005 -0700\n+\n+    13th\n+\n+Notes (other):\n+    yet another note\n+$whitespace\n+    yet another note\n+\n+commit 7038787dfe22a14c3867ce816dbba39845359719\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:24:13 2005 -0700\n+\n+    12th\n+\n+Notes (other):\n+    other note\n+$whitespace\n+    yet another note\n+EOF\n+\n+test_expect_success 'git notes copy --stdin' '\n+\t(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \\\n+\techo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |\n+\tgit notes copy --stdin &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect output &&\n+\ttest \"$(git notes list HEAD)\" = \"$(git notes list HEAD~2)\" &&\n+\ttest \"$(git notes list HEAD^)\" = \"$(git notes list HEAD~3)\"\n+'\n+\n test_done\n-- \n1.7.0.2.407.g21ebda\n"},{"id":"136522","messageId":"cf0765f47b3a97061997125d4440a475cda03e22.1268229087.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1268229087.git.trast@student.ethz.ch","subject":"[PATCH v6 08/13] notes: implement helpers needed for note copying during rewrite","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-03-10T14:05:55Z","receivedAt":"2010-03-10T14:05:55Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Implement helper functions to load the rewriting config, and to\nactually copy the notes.  Also document the config.\n\nSecondly, also implement an undocumented --for-rewrite=<cmd> option to\n'git notes copy' which is used like --stdin, but also puts the\nconfiguration for <cmd> into effect.  It will be needed to support the\ncopying in git-rebase.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\n\n Documentation/config.txt    |   30 +++++++\n Documentation/git-notes.txt |    4 +\n Documentation/githooks.txt  |    4 +\n builtin.h                   |   18 ++++\n builtin/notes.c             |  139 +++++++++++++++++++++++++++++--\n cache.h                     |    2 +\n t/t3301-notes.sh            |  195 +++++++++++++++++++++++++++++++++++++++++++\n t/test-lib.sh               |    2 +\n 8 files changed, 386 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 0d0aa9c..25da59e 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1360,6 +1360,36 @@ The effective value of \"core.notesRef\" (possibly overridden by\n GIT_NOTES_REF) is also implicitly added to the list of refs to be\n displayed.\n \n+notes.rewrite.<command>::\n+\tWhen rewriting commits with <command> (currently `amend` or\n+\t`rebase`) and this variable is set to `true`, git\n+\tautomatically copies your notes from the original to the\n+\trewritten commit.  Defaults to `true`, but see\n+\t\"notes.rewriteRef\" below.\n++\n+This setting can be overridden with the `GIT_NOTES_REWRITE_REF`\n+environment variable, which must be a colon separated list of refs or\n+globs.\n+\n+notes.rewriteMode::\n+\tWhen copying notes during a rewrite (see the\n+\t\"notes.rewrite.<command>\" option), determines what to do if\n+\tthe target commit already has a note.  Must be one of\n+\t`overwrite`, `concatenate`, or `ignore`.  Defaults to\n+\t`concatenate`.\n++\n+This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`\n+environment variable.\n+\n+notes.rewriteRef::\n+\tWhen copying notes during a rewrite, specifies the (fully\n+\tqualified) ref whose notes should be copied.  The ref may be a\n+\tglob, in which case notes in all matching refs will be copied.\n+\tYou may also specify this configuration several times.\n++\n+Does not have a default value; you must configure this variable to\n+enable note rewriting.\n+\n pack.window::\n \tThe size of the window used by linkgit:git-pack-objects[1] when no\n \twindow size is given on the command line. Defaults to 10.\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex 064758b..77311bd 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -35,6 +35,10 @@ This command always manipulates the notes specified in \"core.notesRef\"\n To change which notes are shown by 'git-log', see the\n \"notes.displayRef\" configuration.\n \n+See the description of \"notes.rewrite.<command>\" in\n+linkgit:git-config[1] for a way of carrying your notes across commands\n+that rewrite commits.\n+\n \n SUBCOMMANDS\n -----------\ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex a741769..7183aa9 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -335,6 +335,10 @@ The 'extra-info' is again command-dependent.  If it is empty, the\n preceding SP is also omitted.  Currently, no commands pass any\n 'extra-info'.\n \n+The hook always runs after the automatic note copying (see\n+\"notes.rewrite.<command>\" in linkgit:git-config.txt) has happened, and\n+thus has access to these notes.\n+\n The following command-specific comments apply:\n \n rebase::\ndiff --git a/builtin.h b/builtin.h\nindex cdf9847..8aebe61 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -20,6 +20,24 @@ extern int commit_tree(const char *msg, unsigned char *tree,\n \t\tstruct commit_list *parents, unsigned char *ret,\n \t\tconst char *author);\n extern int commit_notes(struct notes_tree *t, const char *msg);\n+\n+struct notes_rewrite_cfg\n+{\n+\tstruct notes_tree **trees;\n+\tconst char *cmd;\n+\tint enabled;\n+\tcombine_notes_fn *combine;\n+\tstruct string_list *refs;\n+\tint refs_from_env;\n+\tint mode_from_env;\n+};\n+\n+combine_notes_fn *parse_combine_notes_fn(const char *v);\n+struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd);\n+int copy_note_for_rewrite(struct notes_rewrite_cfg *c,\n+\t\t\t  const unsigned char *from_obj, const unsigned char *to_obj);\n+void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);\n+\n extern int check_pager_config(const char *cmd);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex 576a989..6c2297a 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -16,6 +16,7 @@\n #include \"exec_cmd.h\"\n #include \"run-command.h\"\n #include \"parse-options.h\"\n+#include \"string-list.h\"\n \n static const char * const git_notes_usage[] = {\n \t\"git notes [list [<object>]]\",\n@@ -269,14 +270,121 @@ int commit_notes(struct notes_tree *t, const char *msg)\n \treturn 0;\n }\n \n-int notes_copy_from_stdin(int force)\n+\n+combine_notes_fn *parse_combine_notes_fn(const char *v)\n+{\n+\tif (!strcasecmp(v, \"overwrite\"))\n+\t\treturn combine_notes_overwrite;\n+\telse if (!strcasecmp(v, \"ignore\"))\n+\t\treturn combine_notes_ignore;\n+\telse if (!strcasecmp(v, \"concatenate\"))\n+\t\treturn combine_notes_concatenate;\n+\telse\n+\t\treturn NULL;\n+}\n+\n+static int notes_rewrite_config(const char *k, const char *v, void *cb)\n+{\n+\tstruct notes_rewrite_cfg *c = cb;\n+\tif (!prefixcmp(k, \"notes.rewrite.\") && !strcmp(k+14, c->cmd)) {\n+\t\tc->enabled = git_config_bool(k, v);\n+\t\treturn 0;\n+\t} else if (!c->mode_from_env && !strcmp(k, \"notes.rewritemode\")) {\n+\t\tif (!v)\n+\t\t\tconfig_error_nonbool(k);\n+\t\tc->combine = parse_combine_notes_fn(v);\n+\t\tif (!c->combine) {\n+\t\t\terror(\"Bad notes.rewriteMode value: '%s'\", v);\n+\t\t\treturn 1;\n+\t\t}\n+\t\treturn 0;\n+\t} else if (!c->refs_from_env && !strcmp(k, \"notes.rewriteref\")) {\n+\t\t/* note that a refs/ prefix is implied in the\n+\t\t * underlying for_each_glob_ref */\n+\t\tif (!prefixcmp(v, \"refs/notes/\"))\n+\t\t\tstring_list_add_refs_by_glob(c->refs, v);\n+\t\telse\n+\t\t\twarning(\"Refusing to rewrite notes in %s\"\n+\t\t\t\t\" (outside of refs/notes/)\", v);\n+\t\treturn 0;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+\n+struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)\n+{\n+\tstruct notes_rewrite_cfg *c = xmalloc(sizeof(struct notes_rewrite_cfg));\n+\tconst char *rewrite_mode_env = getenv(GIT_NOTES_REWRITE_MODE_ENVIRONMENT);\n+\tconst char *rewrite_refs_env = getenv(GIT_NOTES_REWRITE_REF_ENVIRONMENT);\n+\tc->cmd = cmd;\n+\tc->enabled = 1;\n+\tc->combine = combine_notes_concatenate;\n+\tc->refs = xcalloc(1, sizeof(struct string_list));\n+\tc->refs->strdup_strings = 1;\n+\tc->refs_from_env = 0;\n+\tc->mode_from_env = 0;\n+\tif (rewrite_mode_env) {\n+\t\tc->mode_from_env = 1;\n+\t\tc->combine = parse_combine_notes_fn(rewrite_mode_env);\n+\t\tif (!c->combine)\n+\t\t\terror(\"Bad \" GIT_NOTES_REWRITE_MODE_ENVIRONMENT\n+\t\t\t      \" value: '%s'\", rewrite_mode_env);\n+\t}\n+\tif (rewrite_refs_env) {\n+\t\tc->refs_from_env = 1;\n+\t\tstring_list_add_refs_from_colon_sep(c->refs, rewrite_refs_env);\n+\t}\n+\tgit_config(notes_rewrite_config, c);\n+\tif (!c->enabled || !c->refs->nr) {\n+\t\tstring_list_clear(c->refs, 0);\n+\t\tfree(c->refs);\n+\t\tfree(c);\n+\t\treturn NULL;\n+\t}\n+\tc->trees = load_notes_trees(c->refs);\n+\tstring_list_clear(c->refs, 0);\n+\tfree(c->refs);\n+\treturn c;\n+}\n+\n+int copy_note_for_rewrite(struct notes_rewrite_cfg *c,\n+\t\t\t  const unsigned char *from_obj, const unsigned char *to_obj)\n+{\n+\tint ret = 0;\n+\tint i;\n+\tfor (i = 0; c->trees[i]; i++)\n+\t\tret = copy_note(c->trees[i], from_obj, to_obj, 1, c->combine) || ret;\n+\treturn ret;\n+}\n+\n+void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c)\n+{\n+\tint i;\n+\tfor (i = 0; c->trees[i]; i++) {\n+\t\tcommit_notes(c->trees[i], \"Notes added by 'git notes copy'\");\n+\t\tfree_notes(c->trees[i]);\n+\t}\n+\tfree(c->trees);\n+\tfree(c);\n+}\n+\n+int notes_copy_from_stdin(int force, const char *rewrite_cmd)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n+\tstruct notes_rewrite_cfg *c = NULL;\n \tstruct notes_tree *t;\n \tint ret = 0;\n \n-\tinit_notes(NULL, NULL, NULL, 0);\n-\tt = &default_notes_tree;\n+\tif (rewrite_cmd) {\n+\t\tc = init_copy_notes_for_rewrite(rewrite_cmd);\n+\t\tif (!c)\n+\t\t\treturn 0;\n+\t} else {\n+\t\tinit_notes(NULL, NULL, NULL, 0);\n+\t\tt = &default_notes_tree;\n+\t}\n \n \twhile (strbuf_getline(&buf, stdin, '\\n') != EOF) {\n \t\tunsigned char from_obj[20], to_obj[20];\n@@ -293,7 +401,11 @@ int notes_copy_from_stdin(int force)\n \t\tif (get_sha1(split[1]->buf, to_obj))\n \t\t\tdie(\"Failed to resolve '%s' as a valid ref.\", split[1]->buf);\n \n-\t\terr = copy_note(t, from_obj, to_obj, force, combine_notes_overwrite);\n+\t\tif (rewrite_cmd)\n+\t\t\terr = copy_note_for_rewrite(c, from_obj, to_obj);\n+\t\telse\n+\t\t\terr = copy_note(t, from_obj, to_obj, force,\n+\t\t\t\t\tcombine_notes_overwrite);\n \n \t\tif (err) {\n \t\t\terror(\"Failed to copy notes from '%s' to '%s'\",\n@@ -304,8 +416,12 @@ int notes_copy_from_stdin(int force)\n \t\tstrbuf_list_free(split);\n \t}\n \n-\tcommit_notes(t, \"Notes added by 'git notes copy'\");\n-\tfree_notes(t);\n+\tif (!rewrite_cmd) {\n+\t\tcommit_notes(t, \"Notes added by 'git notes copy'\");\n+\t\tfree_notes(t);\n+\t} else {\n+\t\tfinish_copy_notes_for_rewrite(c);\n+\t}\n \treturn ret;\n }\n \n@@ -321,6 +437,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t    remove = 0, prune = 0, force = 0, from_stdin = 0;\n \tint given_object = 0, i = 1, retval = 0;\n \tstruct msg_arg msg = { 0, 0, STRBUF_INIT };\n+\tconst char *rewrite_cmd = NULL;\n \tstruct option options[] = {\n \t\tOPT_GROUP(\"Notes contents options\"),\n \t\t{ OPTION_CALLBACK, 'm', \"message\", &msg, \"MSG\",\n@@ -338,6 +455,8 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\tOPT_GROUP(\"Other options\"),\n \t\tOPT_BOOLEAN('f', \"force\", &force, \"replace existing notes\"),\n \t\tOPT_BOOLEAN(0, \"stdin\", &from_stdin, \"read objects from stdin\"),\n+\t\tOPT_STRING(0, \"for-rewrite\", &rewrite_cmd, \"command\",\n+\t\t\t   \"load rewriting config for <command> (implies --stdin)\"),\n \t\tOPT_END()\n \t};\n \n@@ -386,6 +505,10 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(git_notes_usage, options);\n \t}\n \n+\tif (!copy && rewrite_cmd) {\n+\t\terror(\"cannot use --for-rewrite with %s subcommand.\", argv[0]);\n+\t\tusage_with_options(git_notes_usage, options);\n+\t}\n \tif (!copy && from_stdin) {\n \t\terror(\"cannot use --stdin with %s subcommand.\", argv[0]);\n \t\tusage_with_options(git_notes_usage, options);\n@@ -393,12 +516,12 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \n \tif (copy) {\n \t\tconst char *from_ref;\n-\t\tif (from_stdin) {\n+\t\tif (from_stdin || rewrite_cmd) {\n \t\t\tif (argc > 1) {\n \t\t\t\terror(\"too many parameters\");\n \t\t\t\tusage_with_options(git_notes_usage, options);\n \t\t\t} else {\n-\t\t\t\treturn notes_copy_from_stdin(force);\n+\t\t\t\treturn notes_copy_from_stdin(force, rewrite_cmd);\n \t\t\t}\n \t\t}\n \t\tif (argc < 3) {\ndiff --git a/cache.h b/cache.h\nindex b25d180..32c18b1 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -388,6 +388,8 @@ static inline enum object_type object_type(unsigned int mode)\n #define GIT_NOTES_REF_ENVIRONMENT \"GIT_NOTES_REF\"\n #define GIT_NOTES_DEFAULT_REF \"refs/notes/commits\"\n #define GIT_NOTES_DISPLAY_REF_ENVIRONMENT \"GIT_NOTES_DISPLAY_REF\"\n+#define GIT_NOTES_REWRITE_REF_ENVIRONMENT \"GIT_NOTES_REWRITE_REF\"\n+#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT \"GIT_NOTES_REWRITE_MODE\"\n \n /*\n  * Repository-local GIT_* environment variables\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex 29ef0c6..a4a0b1d 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -811,4 +811,199 @@ test_expect_success 'git notes copy --stdin' '\n \ttest \"$(git notes list HEAD^)\" = \"$(git notes list HEAD~3)\"\n '\n \n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:26:13 2005 -0700\n+\n+    14th\n+EOF\n+\n+test_expect_success 'git notes copy --for-rewrite (unconfigured)' '\n+\ttest_commit 14th &&\n+\ttest_commit 15th &&\n+\t(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \\\n+\techo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes (other):\n+    yet another note\n+$whitespace\n+    yet another note\n+\n+commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:26:13 2005 -0700\n+\n+    14th\n+\n+Notes (other):\n+    other note\n+$whitespace\n+    yet another note\n+EOF\n+\n+test_expect_success 'git notes copy --for-rewrite (enabled)' '\n+\tgit config notes.rewriteMode overwrite &&\n+\tgit config notes.rewriteRef \"refs/notes/*\" &&\n+\t(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \\\n+\techo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'git notes copy --for-rewrite (disabled)' '\n+\tgit config notes.rewrite.bar false &&\n+\techo $(git rev-parse HEAD~3) $(git rev-parse HEAD) |\n+\tgit notes copy --for-rewrite=bar &&\n+\tgit log -2 > output &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes (other):\n+    a fresh note\n+EOF\n+\n+test_expect_success 'git notes copy --for-rewrite (overwrite)' '\n+\tgit notes add -f -m\"a fresh note\" HEAD^ &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'git notes copy --for-rewrite (ignore)' '\n+\tgit config notes.rewriteMode ignore &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes (other):\n+    a fresh note\n+    another fresh note\n+EOF\n+\n+test_expect_success 'git notes copy --for-rewrite (append)' '\n+\tgit notes add -f -m\"another fresh note\" HEAD^ &&\n+\tgit config notes.rewriteMode concatenate &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes (other):\n+    a fresh note\n+    another fresh note\n+    append 1\n+    append 2\n+EOF\n+\n+test_expect_success 'git notes copy --for-rewrite (append two to one)' '\n+\tgit notes add -f -m\"append 1\" HEAD^ &&\n+\tgit notes add -f -m\"append 2\" HEAD^^ &&\n+\t(echo $(git rev-parse HEAD^) $(git rev-parse HEAD);\n+\techo $(git rev-parse HEAD^^) $(git rev-parse HEAD)) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'git notes copy --for-rewrite (append empty)' '\n+\tgit notes remove HEAD^ &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tgit notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes (other):\n+    replacement note 1\n+EOF\n+\n+test_expect_success 'GIT_NOTES_REWRITE_MODE works' '\n+\tgit notes add -f -m\"replacement note 1\" HEAD^ &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tGIT_NOTES_REWRITE_MODE=overwrite git notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect << EOF\n+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes (other):\n+    replacement note 2\n+EOF\n+\n+test_expect_success 'GIT_NOTES_REWRITE_REF works' '\n+\tgit config notes.rewriteMode overwrite &&\n+\tgit notes add -f -m\"replacement note 2\" HEAD^ &&\n+\tgit config --unset-all notes.rewriteRef &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tGIT_NOTES_REWRITE_REF=refs/notes/commits:refs/notes/other \\\n+\t\tgit notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'GIT_NOTES_REWRITE_REF overrides config' '\n+\tgit config notes.rewriteRef refs/notes/other &&\n+\tgit notes add -f -m\"replacement note 3\" HEAD^ &&\n+\techo $(git rev-parse HEAD^) $(git rev-parse HEAD) |\n+\tGIT_NOTES_REWRITE_REF= git notes copy --for-rewrite=foo &&\n+\tgit log -1 > output &&\n+\ttest_cmp expect output\n+'\n test_done\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 3d026b4..c582964 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -56,6 +56,8 @@ unset SHA1_FILE_DIRECTORIES\n unset SHA1_FILE_DIRECTORY\n unset GIT_NOTES_REF\n unset GIT_NOTES_DISPLAY_REF\n+unset GIT_NOTES_REWRITE_REF\n+unset GIT_NOTES_REWRITE_MODE\n GIT_MERGE_VERBOSITY=5\n export GIT_MERGE_VERBOSITY\n export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME\n-- \n1.7.0.2.407.g21ebda\n"},{"id":"136518","messageId":"78cd4b3f89c0423c8f680da3de0f29ad2d492a4b.1268229087.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1268229087.git.trast@student.ethz.ch","subject":"[PATCH v6 09/13] rebase: support automatic notes copying","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-03-10T14:05:56Z","receivedAt":"2010-03-10T14:05:56Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Luckily, all the support already happens to be there.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\n\n git-am.sh                     |    5 ++++-\n git-rebase--interactive.sh    |    4 ++++\n git-rebase.sh                 |    1 +\n t/t3400-rebase.sh             |   17 +++++++++++++++++\n t/t3404-rebase-interactive.sh |   24 ++++++++++++++++++++++++\n 5 files changed, 50 insertions(+), 1 deletions(-)\n\ndiff --git a/git-am.sh b/git-am.sh\nindex 9b73e0b..1056075 100755\n--- a/git-am.sh\n+++ b/git-am.sh\n@@ -797,8 +797,11 @@ do\n \tgo_next\n done\n \n-if test -s \"$dotest\"/rewritten && test -x \"$GIT_DIR\"/hooks/post-rewrite; then\n+if test -s \"$dotest\"/rewritten; then\n+    git notes copy --for-rewrite=rebase < \"$dotest\"/rewritten\n+    if test -x \"$GIT_DIR\"/hooks/post-rewrite; then\n \t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$dotest\"/rewritten\n+    fi\n fi\n \n rm -fr \"$dotest\"\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex f18a118..a57f043 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -561,6 +561,10 @@ do_next () {\n \t\ttest ! -f \"$DOTEST\"/verbose ||\n \t\t\tgit diff-tree --stat $(cat \"$DOTEST\"/head)..HEAD\n \t} &&\n+\t{\n+\t\tgit notes copy --for-rewrite=rebase < \"$REWRITTEN_LIST\" ||\n+\t\ttrue # we don't care if this copying failed\n+\t} &&\n \tif test -x \"$GIT_DIR\"/hooks/post-rewrite &&\n \t\ttest -s \"$REWRITTEN_LIST\"; then\n \t\t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$REWRITTEN_LIST\"\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex 52f8b9b..e0eb956 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -152,6 +152,7 @@ move_to_original_branch () {\n \n finish_rb_merge () {\n \tmove_to_original_branch\n+\tgit notes copy --for-rewrite=rebase < \"$dotest\"/rewritten\n \tif test -x \"$GIT_DIR\"/hooks/post-rewrite &&\n \t\ttest -s \"$dotest\"/rewritten; then\n \t\t\"$GIT_DIR\"/hooks/post-rewrite rebase < \"$dotest\"/rewritten\ndiff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh\nindex 4314ad2..dbf7dfb 100755\n--- a/t/t3400-rebase.sh\n+++ b/t/t3400-rebase.sh\n@@ -151,4 +151,21 @@ test_expect_success 'Rebase a commit that sprinkles CRs in' '\n \tgit diff --exit-code file-with-cr:CR HEAD:CR\n '\n \n+test_expect_success 'rebase can copy notes' '\n+\tgit config notes.rewrite.rebase true &&\n+\tgit config notes.rewriteRef \"refs/notes/*\" &&\n+\ttest_commit n1 &&\n+\ttest_commit n2 &&\n+\ttest_commit n3 &&\n+\tgit notes add -m\"a note\" n3 &&\n+\tgit rebase --onto n1 n2 &&\n+\ttest \"a note\" = \"$(git notes show HEAD)\"\n+'\n+\n+test_expect_success 'rebase -m can copy notes' '\n+\tgit reset --hard n3 &&\n+\tgit rebase -m --onto n1 n2 &&\n+\ttest \"a note\" = \"$(git notes show HEAD)\"\n+'\n+\n test_done\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 4e35137..19668c2 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -553,4 +553,28 @@ test_expect_success 'reword' '\n \tgit show HEAD~2 | grep \"C changed\"\n '\n \n+test_expect_success 'rebase -i can copy notes' '\n+\tgit config notes.rewrite.rebase true &&\n+\tgit config notes.rewriteRef \"refs/notes/*\" &&\n+\ttest_commit n1 &&\n+\ttest_commit n2 &&\n+\ttest_commit n3 &&\n+\tgit notes add -m\"a note\" n3 &&\n+\tgit rebase --onto n1 n2 &&\n+\ttest \"a note\" = \"$(git notes show HEAD)\"\n+'\n+\n+cat >expect <<EOF\n+an earlier note\n+a note\n+EOF\n+\n+test_expect_success 'rebase -i can copy notes over a fixup' '\n+\tgit reset --hard n3 &&\n+\tgit notes add -m\"an earlier note\" n2 &&\n+\tGIT_NOTES_REWRITE_MODE=concatenate FAKE_LINES=\"1 fixup 2\" git rebase -i n1 &&\n+\tgit notes show > output &&\n+\ttest_cmp expect output\n+'\n+\n test_done\n-- \n1.7.0.2.407.g21ebda\n"},{"id":"136524","messageId":"42553bc69ac137c8aaacc31eede974366af3d392.1268229087.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1268229087.git.trast@student.ethz.ch","subject":"[PATCH v6 10/13] commit --amend: copy notes to the new commit","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-03-10T14:05:57Z","receivedAt":"2010-03-10T14:05:57Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Teaches 'git commit --amend' to copy notes.  The catch is that this\nmust also be guarded by --no-post-rewrite, which we use to prevent\n--amend from copying notes during a rebase -i 'edit'/'reword'.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\n\n builtin/commit.c  |    6 ++++++\n t/t7501-commit.sh |   12 ++++++++++++\n 2 files changed, 18 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 0367412..8dd104e 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1340,6 +1340,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \trerere(0);\n \trun_hook(get_index_file(), \"post-commit\", NULL);\n \tif (amend && !no_post_rewrite) {\n+\t\tstruct notes_rewrite_cfg *cfg;\n+\t\tcfg = init_copy_notes_for_rewrite(\"amend\");\n+\t\tif (cfg) {\n+\t\t\tcopy_note_for_rewrite(cfg, head_sha1, commit_sha1);\n+\t\t\tfinish_copy_notes_for_rewrite(cfg);\n+\t\t}\n \t\trun_rewrite_hook(head_sha1, commit_sha1);\n \t}\n \tif (!quiet)\ndiff --git a/t/t7501-commit.sh b/t/t7501-commit.sh\nindex 7940901..8297cb4 100755\n--- a/t/t7501-commit.sh\n+++ b/t/t7501-commit.sh\n@@ -425,4 +425,16 @@ test_expect_success 'amend using the message from a commit named with tag' '\n \n '\n \n+test_expect_success 'amend can copy notes' '\n+\n+\tgit config notes.rewrite.amend true &&\n+\tgit config notes.rewriteRef \"refs/notes/*\" &&\n+\ttest_commit foo &&\n+\tgit notes add -m\"a note\" &&\n+\ttest_tick &&\n+\tgit commit --amend -m\"new foo\" &&\n+\ttest \"$(git notes show)\" = \"a note\"\n+\n+'\n+\n test_done\n-- \n1.7.0.2.407.g21ebda\n"},{"id":"136521","messageId":"dd44a5b7ef528c37293a0aa3df2e5016938d23fa.1268229087.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1268229087.git.trast@student.ethz.ch","subject":"[PATCH v6 11/13] notes: add shorthand --ref to override GIT_NOTES_REF","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-03-10T14:05:58Z","receivedAt":"2010-03-10T14:05:58Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Adds a shorthand option that overrides the GIT_NOTES_REF variable, and\nhence determines the notes tree that will be manipulated.  It also\nDWIMs a refs/notes/ prefix.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\n\n Documentation/git-notes.txt |    5 +++++\n builtin/notes.c             |   16 ++++++++++++++++\n 2 files changed, 21 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex 77311bd..02a2baf 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -116,6 +116,11 @@ OPTIONS\n \tLike '-C', but with '-c' the editor is invoked, so that\n \tthe user can further edit the note message.\n \n+--ref <ref>::\n+\tManipulate the notes tree in <ref>.  This overrides both\n+\tGIT_NOTES_REF and the \"core.notesRef\" configuration.  The ref\n+\tis taken to be in `refs/notes/` if it is not qualified.\n+\n Author\n ------\n Written by Johannes Schindelin <johannes.schindelin@gmx.de> and\ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex 6c2297a..cb30ad0 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -438,6 +438,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \tint given_object = 0, i = 1, retval = 0;\n \tstruct msg_arg msg = { 0, 0, STRBUF_INIT };\n \tconst char *rewrite_cmd = NULL;\n+\tconst char *override_notes_ref = NULL;\n \tstruct option options[] = {\n \t\tOPT_GROUP(\"Notes contents options\"),\n \t\t{ OPTION_CALLBACK, 'm', \"message\", &msg, \"MSG\",\n@@ -455,6 +456,8 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \t\tOPT_GROUP(\"Other options\"),\n \t\tOPT_BOOLEAN('f', \"force\", &force, \"replace existing notes\"),\n \t\tOPT_BOOLEAN(0, \"stdin\", &from_stdin, \"read objects from stdin\"),\n+\t\tOPT_STRING(0, \"ref\", &override_notes_ref, \"notes_ref\",\n+\t\t\t   \"use notes from <notes_ref>\"),\n \t\tOPT_STRING(0, \"for-rewrite\", &rewrite_cmd, \"command\",\n \t\t\t   \"load rewriting config for <command> (implies --stdin)\"),\n \t\tOPT_END()\n@@ -464,6 +467,19 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, options, git_notes_usage, 0);\n \n+\tif (override_notes_ref) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tif (!prefixcmp(override_notes_ref, \"refs/notes/\"))\n+\t\t\t/* we're happy */;\n+\t\telse if (!prefixcmp(override_notes_ref, \"notes/\"))\n+\t\t\tstrbuf_addstr(&sb, \"refs/\");\n+\t\telse\n+\t\t\tstrbuf_addstr(&sb, \"refs/notes/\");\n+\t\tstrbuf_addstr(&sb, override_notes_ref);\n+\t\tsetenv(\"GIT_NOTES_REF\", sb.buf, 1);\n+\t\tstrbuf_release(&sb);\n+\t}\n+\n \tif (argc && !strcmp(argv[0], \"list\"))\n \t\tlist = 1;\n \telse if (argc && !strcmp(argv[0], \"add\"))\n-- \n1.7.0.2.407.g21ebda\n"},{"id":"136519","messageId":"f4ef87cbd1f0bb98d8c8d2cc7dd6d3e417ec2875.1268229087.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1268229087.git.trast@student.ethz.ch","subject":"[PATCH v6 12/13] notes: track whether notes_trees were changed at all","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-03-10T14:05:59Z","receivedAt":"2010-03-10T14:05:59Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Currently, the notes copying is a bit wasteful since it always creates\nnew trees, even if no notes were copied at all.\n\nTeach add_note() and remove_note() to flag the affected notes tree as\nchanged ('dirty').  Then teach builtin/notes.c to use this knowledge\nand avoid committing trees that weren't changed.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nNew in this iteration\n\n builtin/notes.c |    2 ++\n notes.c         |    3 +++\n notes.h         |    1 +\n 3 files changed, 6 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex cb30ad0..4543d11 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -240,6 +240,8 @@ int commit_notes(struct notes_tree *t, const char *msg)\n \t\tt = &default_notes_tree;\n \tif (!t->initialized || !t->ref || !*t->ref)\n \t\tdie(\"Cannot commit uninitialized/unreferenced notes tree\");\n+\tif (!t->dirty)\n+\t\treturn 0; /* don't have to commit an unchanged tree */\n \n \t/* Prepare commit message and reflog message */\n \tstrbuf_addstr(&buf, \"notes: \"); /* commit message starts at index 7 */\ndiff --git a/notes.c b/notes.c\nindex b1b15e9..ffda597 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -926,6 +926,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \tt->ref = notes_ref ? xstrdup(notes_ref) : NULL;\n \tt->combine_notes = combine_notes;\n \tt->initialized = 1;\n+\tt->dirty = 0;\n \n \tif (flags & NOTES_INIT_EMPTY || !notes_ref ||\n \t    read_ref(notes_ref, object_sha1))\n@@ -1014,6 +1015,7 @@ void add_note(struct notes_tree *t, const unsigned char *object_sha1,\n \tif (!t)\n \t\tt = &default_notes_tree;\n \tassert(t->initialized);\n+\tt->dirty = 1;\n \tif (!combine_notes)\n \t\tcombine_notes = t->combine_notes;\n \tl = (struct leaf_node *) xmalloc(sizeof(struct leaf_node));\n@@ -1029,6 +1031,7 @@ void remove_note(struct notes_tree *t, const unsigned char *object_sha1)\n \tif (!t)\n \t\tt = &default_notes_tree;\n \tassert(t->initialized);\n+\tt->dirty = 1;\n \thashcpy(l.key_sha1, object_sha1);\n \thashclr(l.val_sha1);\n \treturn note_tree_remove(t, t->root, 0, &l);\ndiff --git a/notes.h b/notes.h\nindex e1868d8..ee5d47b 100644\n--- a/notes.h\n+++ b/notes.h\n@@ -40,6 +40,7 @@\n \tchar *ref;\n \tcombine_notes_fn *combine_notes;\n \tint initialized;\n+\tint dirty;\n } default_notes_tree;\n \n /*\n-- \n1.7.0.2.407.g21ebda\n"},{"id":"136520","messageId":"2a789ed6300651d06fbe5835184e2669a2c483cc.1268229087.git.trast@student.ethz.ch","threadId":"22744","inReplyTo":"cover.1268229087.git.trast@student.ethz.ch","subject":"[PATCH v6 13/13] git-notes(1): add a section about the meaning of history","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-03-10T14:06:00Z","receivedAt":"2010-03-10T14:06:00Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"To the displaying code, the only interesting thing about a notes ref\nis that it has a tree of the required format.  However, notes actually\nhave a history since they are recorded as successive commits.\n\nMake a note about the existence of this history in the manpage, but\nkeep some doors open if we want to change the details.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nNew in v6, as per Junio's comment\n\n>  - As to the determination of committer/author identity, I think what the\n>    code currently does (i.e. honor the environment and user.* config) is\n>    perfectly sane, but at the same time I think it would be surprising for\n>    unsuspecting users.  We may want to advise users about this in the\n>    documentation.\n\n(but see the series leader for more discussion about this).\n\n Documentation/git-notes.txt |   14 ++++++++++++++\n 1 files changed, 14 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex 02a2baf..4e5113b 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -121,6 +121,20 @@ OPTIONS\n \tGIT_NOTES_REF and the \"core.notesRef\" configuration.  The ref\n \tis taken to be in `refs/notes/` if it is not qualified.\n \n+\n+NOTES\n+-----\n+\n+Every notes change creates a new commit at the specified notes ref.\n+You can therefore inspect the history of the notes by invoking, e.g.,\n+`git log -p notes/commits`.\n+\n+Currently the commit message only records which operation triggered\n+the update, and the commit authorship is determined according to the\n+usual rules (see linkgit:git-commit[1]).  These details may change in\n+the future.\n+\n+\n Author\n ------\n Written by Johannes Schindelin <johannes.schindelin@gmx.de> and\n-- \n1.7.0.2.407.g21ebda\n"},{"id":"136526","messageId":"201003101508.02625.trast@student.ethz.ch","threadId":"22744","inReplyTo":"6cf9010742df96e0c68ef8adc1ab392c08525bc2.1266946262.git.trast@student.ethz.ch","subject":"Re: [PATCH] format-patch: learn to fill comment section of email from notes","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-03-10T14:08:02Z","receivedAt":"2010-03-10T14:08:02Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"I accidentally sent out this and the \"BROKEN\" patch, they're the exact\nones I posted on Feb 23 and should be disregarded.\n\nSorry for the noise...\n\n[And this time I can't even claim it was because I was hacking past\nmidnight :-(]\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"136543","messageId":"7vwrxjrhue.fsf@alter.siamese.dyndns.org","threadId":"22744","inReplyTo":"cover.1268229087.git.trast@student.ethz.ch","subject":"Re: [PATCH v6 00/13] several notes refs, post-rewrite, notes rewriting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-03-10T21:23:53Z","receivedAt":"2010-03-10T21:23:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> I'm not aware of comments by anyone else, so if I missed them please\n> point them out.\n\nYes, please ;-)\n\n>>  - \"git notes add\" and friends honor GIT_COMMITER_* and GIT_AUTHOR_* as\n>>    usual.  Since I use post-applypatch hook to record the message-id from\n>>    \"git am\", I ended up getting my notes written by original submitters.\n> [...]\n>>  - As to the determination of committer/author identity, I think what the\n>>    code currently does (i.e. honor the environment and user.* config) is\n>>    perfectly sane, but at the same time I think it would be surprising for\n>>    unsuspecting users.  We may want to advise users about this in the\n>>    documentation.\n>\n> I added a small patch that writes this and the history recording down\n> in git-notes(1).  However, I'm hesitating to directly say \"and this\n> will bite you if you use it in post-applypatch\"; isn't the latter an\n> implementation detail of git-am?\n\nProbably.  We haven't documented what can and cannot be used by pre- and\npost- applypatch, but I think at least post-applypatch should not rely on\nthe authorship information exported from the caller; if it wants to find\nout, it can parse HEAD, and because it is a post-hook, it should always be\ncorrect no matter how git-am implementation will change in the future.\n"},{"id":"136564","messageId":"201003110955.06170.johan@herland.net","threadId":"22744","inReplyTo":"31001e0ddcf756d542e2c899f61c78e25beaf6fa.1268229087.git.trast@student.ethz.ch","subject":"Re: [PATCH v6 01/13] test-lib: unset GIT_NOTES_REF to stop it from influencing tests","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2010-03-11T08:55:05Z","receivedAt":"2010-03-11T08:55:05Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Wednesday 10 March 2010, Thomas Rast wrote:\n> ---\n> \n> \n>  t/test-lib.sh |    1 +\n>  1 files changed, 1 insertions(+), 0 deletions(-)\n> \n> diff --git a/t/test-lib.sh b/t/test-lib.sh\n> index a0e396a..400bc6b 100644\n> --- a/t/test-lib.sh\n> +++ b/t/test-lib.sh\n> @@ -54,6 +54,7 @@ unset GIT_OBJECT_DIRECTORY\n>  unset GIT_CEILING_DIRECTORIES\n>  unset SHA1_FILE_DIRECTORIES\n>  unset SHA1_FILE_DIRECTORY\n> +unset GIT_NOTES_REF\n>  GIT_MERGE_VERBOSITY=5\n>  export GIT_MERGE_VERBOSITY\n>  export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME\n\nAcked-by: Johan Herland <johan@herland.net>\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"136568","messageId":"201003111103.22671.johan@herland.net","threadId":"22744","inReplyTo":"e69a916ca6afb53fb665951d499d7e6543007008.1268229087.git.trast@student.ethz.ch","subject":"Re: [PATCH v6 02/13] Support showing notes from more than one notes tree","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2010-03-11T10:03:22Z","receivedAt":"2010-03-11T10:03:22Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Wednesday 10 March 2010, Thomas Rast wrote:\n> With this patch, you can set notes.displayRef to a glob that points at\n> your favourite notes refs, e.g.,\n> \n> [notes]\n> \tdisplayRef = refs/notes/*\n> \n> Then git-log and friends will show notes from all trees.\n> \n> Thanks to Junio C Hamano for lots of feedback, which greatly\n> influenced the design of the entire series and this commit in\n> particular.\n> \n> Signed-off-by: Thomas Rast <trast@student.ethz.ch>\n\nAcked-by: Johan Herland <johan@herland.net>\n\n...except for the following small niggles:\n\n> diff --git a/Documentation/pretty-options.txt\n> b/Documentation/pretty-options.txt index aa96cae..1d56926 100644\n> --- a/Documentation/pretty-options.txt\n> +++ b/Documentation/pretty-options.txt\n> @@ -30,9 +30,18 @@ people using 80-column terminals.\n>  \tdefaults to UTF-8.\n> \n>  --no-notes::\n> ---show-notes::\n> +--show-notes[=<ref>]::\n>  \tShow the notes (see linkgit:git-notes[1]) that annotate the\n>  \tcommit, when showing the commit log message.  This is the default\n>  \tfor `git log`, `git show` and `git whatchanged` commands when\n>  \tthere is no `--pretty`, `--format` nor `--oneline` option is\n>  \tgiven on the command line.\n> ++\n> +With an optional argument, add this ref to the list of notes.  The ref\n> +is taken to be in `refs/notes/` if it is not qualified.\n> +\n> +--[no-]standard-notes::\n> +\tEnable or disable populating the notes ref list from the\n> +\t'core.notesRef' and 'notes.displayRef' variables (or\n> +\tcorresponding environment overrides).  See\n> +\tlinkgit:git-config[1].\n\nShould probably mention that --standard-notes describes the default \nbehaviour.\n\n> diff --git a/notes.c b/notes.c\n> index a4f9926..0d4b892 100644\n> --- a/notes.c\n> +++ b/notes.c\n> +static int notes_display_config(const char *k, const char *v, void *cb)\n> +{\n> +\tint *load_refs = cb;\n> +\n> +\tif (*load_refs && !strcmp(k, \"notes.displayref\")) {\n> +\t\tif (!v)\n> +\t\t\tconfig_error_nonbool(k);\n> +\t\tstring_list_add_refs_by_glob(&display_notes_refs, v);\n> +\t\treturn 0;\n\nThis \"return 0\" is unnecessary\n\n> +static int string_list_add_refs_from_list(struct string_list_item *item,\n> +\t\t\t\t\t  void *cb)\n> +{\n> +\tstruct string_list *list = cb;\n> +\tstring_list_add_refs_by_glob(list, item->string);\n> +\treturn 0;\n> +}\n\nShouldn't string_list_add_refs_from_list() be placed up with the other \nstring_list_add_* functions? \n\n> +void init_display_notes(struct display_notes_opt *opt)\n> +{\n> +\tchar *display_ref_env;\n> +\tint load_config_refs = 0;\n> +\tdisplay_notes_refs.strdup_strings = 1;\n> +\n> +\tassert(!display_notes_trees);\n> +\n> +\tif (!opt || !opt->suppress_default_notes) {\n> +\t\tstring_list_append(default_notes_ref(), &display_notes_refs);\n> +\t\tdisplay_ref_env = getenv(GIT_NOTES_DISPLAY_REF_ENVIRONMENT);\n> +\t\tif (display_ref_env) {\n> +\t\t\tstring_list_add_refs_from_colon_sep(&display_notes_refs,\n> +\t\t\t\t\t\t\t    display_ref_env);\n> +\t\t\tload_config_refs = 0;\n> +\t\t} else {\n> +\t\t\tload_config_refs = 1;\n> +\t\t}\n\nDrop unnecessary braces in the 'else'\n\n> @@ -1030,3 +1178,14 @@ void format_note(struct notes_tree *t, const\n> unsigned char *object_sha1,\n> \n>  \tfree(msg);\n>  }\n> +\n> +void format_display_notes(const unsigned char *object_sha1,\n> +\t\t\t  struct strbuf *sb, const char *output_encoding, int flags)\n> +{\n> +\tint i;\n> +\tif (!display_notes_trees)\n> +\t\tinit_display_notes(NULL);\n\nNot sure I like this \"fallback\" call to init_display_notes(NULL). There's \nalready a call from builtin/log.c which passes \"&rev->notes_opt\", and this \nfallback is at best useless (since init_display_notes() has already been \ncalled from builtin/log.c), and at worst confusing, since it would _mask_ a \nmissing init_display_notes(options) call from elsewhere.\n\nI'd rather suggest that format_display_notes() should fail because of a \nmissing init_display_notes(), and not silently compensate for missing \ninitialization by self-initializing with default options (thus disregarding \nany notes-related options the user might have passed on the command-line).\n\n> diff --git a/notes.h b/notes.h\n> index bad03cc..db47b67 100644\n> --- a/notes.h\n> +++ b/notes.h\n> +/*\n> + * Append notes for the given 'object_sha1' from all trees set up by\n> + * init_display_notes() to 'sb'.  The 'flags' are a bitwise\n> + * combination of\n> + *\n> + * - NOTES_SHOW_HEADER: add a 'Notes (refname):' header\n> + *\n> + * - NOTES_INDENT: indent the notes by 4 places\n> + *\n> + * init_display_notes() is called implicitly for you if you haven't\n> + * already.\n\nAs I said above, the implicit initialization turns a missing \ninit_display_notes(with_approriate_options) call into a \ninit_display_notes(NULL) call. This makes it harder to pinpoint future bugs \nin this area (which are likely to crop up as we add notes functionality to \nmore Git commands).\n\nBut I don't think this is important enough to hold up the patch series.\n\n> + */\n> +void format_display_notes(const unsigned char *object_sha1,\n> +\t\t\t  struct strbuf *sb, const char *output_encoding, int flags);\n> +\n> +/*\n> + * Load the notes tree from each ref listed in 'refs'.  The output is\n> + * an array of notes_tree*, terminated by a NULL.\n> + */\n> +struct notes_tree **load_notes_trees(struct string_list *refs);\n\nAlthough this looks like a useful utility function, I'm not sure how useful \nit is in practice: What would you really _do_ with the returned struct \nnotes_tree **? You can't pass it to format_display_notes() (which only uses \nthe internal display_notes_trees)...\n\n> +/*\n> + * Add all refs that match 'glob' to the 'list'.\n> + */\n> +void string_list_add_refs_by_glob(struct string_list *list, const char\n> *glob); +\n> +/*\n> + * Add all refs from a colon-separated glob list 'globs' to the end of\n> + * 'list'.  Empty components are ignored.  This helper is used to\n> + * parse GIT_NOTES_DISPLAY_REF style environment variables.\n> + */\n> +void string_list_add_refs_from_colon_sep(struct string_list *list,\n> +\t\t\t\t\t const char *globs);\n\nThe same goes for these. Do they have actual outside callers? AFAICS we only \never use the internal display_notes_refs and display_notes_trees lists in \nnotes.c, so there's limited potential for external use.\n\n\nOtherwise, it all looks good to me. :)\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"136569","messageId":"201003111130.42095.johan@herland.net","threadId":"22744","inReplyTo":"a6795d8aa8dd21236cca0fc61e55d3d306465e62.1268229087.git.trast@student.ethz.ch","subject":"Re: [PATCH v6 07/13] notes: implement 'git notes copy --stdin'","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2010-03-11T10:30:41Z","receivedAt":"2010-03-11T10:30:41Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Wednesday 10 March 2010, Thomas Rast wrote:\n> This implements a mass-copy command that takes a sequence of lines in\n> the format\n> \n>   <from-sha1> SP <to-sha1> [ SP <rest> ] LF\n> \n> on stdin, and copies each <from-sha1>'s notes to the <to-sha1>.  The\n> <rest> is ignored.  The intent, of course, is that this can read the\n> same input that the 'post-rewrite' hook gets.\n> \n> The copy_note() function is exposed for everyone's and in particular\n> the next commit's use.\n> \n> Signed-off-by: Thomas Rast <trast@student.ethz.ch>\n\nAcked-by: Johan Herland <johan@herland.net>\n\n> ---\n> \n> Changes since v5\n> * don't remove note we know doesn't exist\n\n[...]\n\n> diff --git a/notes.c b/notes.c\n> index 0d4b892..b1b15e9 100644\n> --- a/notes.c\n> +++ b/notes.c\n> @@ -1189,3 +1189,22 @@ void format_display_notes(const unsigned char\n> *object_sha1, format_note(display_notes_trees[i], object_sha1, sb,\n>  \t\t\t    output_encoding, flags);\n>  }\n> +\n> +int copy_note(struct notes_tree *t,\n> +\t      const unsigned char *from_obj, const unsigned char *to_obj,\n> +\t      int force, combine_notes_fn combine_fn)\n> +{\n> +\tconst unsigned char *note = get_note(t, from_obj);\n> +\tconst unsigned char *existing_note = get_note(t, to_obj);\n> +\n> +\tif (!force && existing_note)\n> +\t\treturn 1;\n> +\n> +\tif (note)\n> +\t\tadd_note(t, to_obj, note, combine_fn);\n> +\telse if (existing_note) {\n> +\t\tadd_note(t, to_obj, null_sha1, combine_fn);\n> +\t}\n\nYou might want to lose the braces around the second add_note().\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"136571","messageId":"201003111150.58740.johan@herland.net","threadId":"22744","inReplyTo":"cf0765f47b3a97061997125d4440a475cda03e22.1268229087.git.trast@student.ethz.ch","subject":"Re: [PATCH v6 08/13] notes: implement helpers needed for note copying during rewrite","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2010-03-11T10:50:58Z","receivedAt":"2010-03-11T10:50:58Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Wednesday 10 March 2010, Thomas Rast wrote:\n> Implement helper functions to load the rewriting config, and to\n> actually copy the notes.  Also document the config.\n> \n> Secondly, also implement an undocumented --for-rewrite=<cmd> option to\n> 'git notes copy' which is used like --stdin, but also puts the\n> configuration for <cmd> into effect.  It will be needed to support the\n> copying in git-rebase.\n> \n> Signed-off-by: Thomas Rast <trast@student.ethz.ch>\n\nAcked-by: Johan Herland <johan@herland.net>\n\n[...]\n\n> diff --git a/builtin/notes.c b/builtin/notes.c\n> index 576a989..6c2297a 100644\n> --- a/builtin/notes.c\n> +++ b/builtin/notes.c\n\n[...]\n\n> +struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)\n> +{\n> +\tstruct notes_rewrite_cfg *c = xmalloc(sizeof(struct\n> notes_rewrite_cfg)); +\tconst char *rewrite_mode_env =\n> getenv(GIT_NOTES_REWRITE_MODE_ENVIRONMENT); +\tconst char\n> *rewrite_refs_env = getenv(GIT_NOTES_REWRITE_REF_ENVIRONMENT); +\tc->cmd\n> = cmd;\n> +\tc->enabled = 1;\n> +\tc->combine = combine_notes_concatenate;\n> +\tc->refs = xcalloc(1, sizeof(struct string_list));\n> +\tc->refs->strdup_strings = 1;\n> +\tc->refs_from_env = 0;\n> +\tc->mode_from_env = 0;\n> +\tif (rewrite_mode_env) {\n> +\t\tc->mode_from_env = 1;\n> +\t\tc->combine = parse_combine_notes_fn(rewrite_mode_env);\n> +\t\tif (!c->combine)\n> +\t\t\terror(\"Bad \" GIT_NOTES_REWRITE_MODE_ENVIRONMENT\n> +\t\t\t      \" value: '%s'\", rewrite_mode_env);\n> +\t}\n> +\tif (rewrite_refs_env) {\n> +\t\tc->refs_from_env = 1;\n> +\t\tstring_list_add_refs_from_colon_sep(c->refs, rewrite_refs_env);\n> +\t}\n> +\tgit_config(notes_rewrite_config, c);\n> +\tif (!c->enabled || !c->refs->nr) {\n> +\t\tstring_list_clear(c->refs, 0);\n> +\t\tfree(c->refs);\n> +\t\tfree(c);\n> +\t\treturn NULL;\n> +\t}\n> +\tc->trees = load_notes_trees(c->refs);\n\nAh, this invalidates some of my comments to PATCH 02/13. Please disregard \nthose comments.\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"136572","messageId":"201003111156.46614.johan@herland.net","threadId":"22744","inReplyTo":"dd44a5b7ef528c37293a0aa3df2e5016938d23fa.1268229087.git.trast@student.ethz.ch","subject":"Re: [PATCH v6 11/13] notes: add shorthand --ref to override GIT_NOTES_REF","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2010-03-11T10:56:46Z","receivedAt":"2010-03-11T10:56:46Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Wednesday 10 March 2010, Thomas Rast wrote:\n> Adds a shorthand option that overrides the GIT_NOTES_REF variable, and\n> hence determines the notes tree that will be manipulated.  It also\n> DWIMs a refs/notes/ prefix.\n> \n> Signed-off-by: Thomas Rast <trast@student.ethz.ch>\n\nAcked-by: Johan Herland <johan@herland.net>\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"136573","messageId":"201003111158.57543.johan@herland.net","threadId":"22744","inReplyTo":"f4ef87cbd1f0bb98d8c8d2cc7dd6d3e417ec2875.1268229087.git.trast@student.ethz.ch","subject":"Re: [PATCH v6 12/13] notes: track whether notes_trees were changed at all","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2010-03-11T10:58:57Z","receivedAt":"2010-03-11T10:58:57Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Wednesday 10 March 2010, Thomas Rast wrote:\n> Currently, the notes copying is a bit wasteful since it always creates\n> new trees, even if no notes were copied at all.\n> \n> Teach add_note() and remove_note() to flag the affected notes tree as\n> changed ('dirty').  Then teach builtin/notes.c to use this knowledge\n> and avoid committing trees that weren't changed.\n> \n> Signed-off-by: Thomas Rast <trast@student.ethz.ch>\n\nAcked-by: Johan Herland <johan@herland.net>\n\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"136574","messageId":"201003111159.24935.johan@herland.net","threadId":"22744","inReplyTo":"2a789ed6300651d06fbe5835184e2669a2c483cc.1268229087.git.trast@student.ethz.ch","subject":"Re: [PATCH v6 13/13] git-notes(1): add a section about the meaning of history","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2010-03-11T10:59:24Z","receivedAt":"2010-03-11T10:59:24Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Wednesday 10 March 2010, Thomas Rast wrote:\n> To the displaying code, the only interesting thing about a notes ref\n> is that it has a tree of the required format.  However, notes actually\n> have a history since they are recorded as successive commits.\n> \n> Make a note about the existence of this history in the manpage, but\n> keep some doors open if we want to change the details.\n> \n> Signed-off-by: Thomas Rast <trast@student.ethz.ch>\n\nAcked-by: Johan Herland <johan@herland.net>\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"}]}