{"thread":{"id":"22652","subject":"[RFC PATCH 1/6] Documentation: document post-rewrite hook","startedAt":"2010-02-14T16:17:05Z","lastAt":"2010-02-20T15:23:58Z","messageCount":34,"participants":["Thomas Rast","Johan Herland","Johannes Sixt","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"134551","messageId":"cover.1266164150.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":null,"subject":"[RFC PATCH 0/6] post-rewrite hook and copying notes","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-14T16:17:05Z","receivedAt":"2010-02-14T16:17:05Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"This series implements a post-rewrite hook.  This has been on my todo\nlist since we first got notes, but I never got around to it.\n\nMy original intended use-case was to add a note for the source of the\nrewritten commit, but recent discussion on git-notes was that we\nshould add some feature that lets users copy over their notes when\nrewriting commits.  The hook in the last patch, together with the 'git\nnotes copy' command now in 'pu', can achieve this.\n\nI'm not sure whether we should stop here and declare it the user's\nresponsibility to enable the hook though.  My idea of a \"full\nintegration\" approach would be to instead teach 'git notes copy' to\nread the same type of lists that I'm handing to post-rewrite, and then\nwire calls to it into the scripts in the same places that call\npost-rewrite.  Opinions?\n\nThomas Rast (6):\n  Documentation: document post-rewrite hook\n  commit --amend: invoke post-rewrite hook\n  filter-branch: invoke post-rewrite hook\n  rebase: invoke post-rewrite hook\n  rebase -i: invoke post-rewrite hook\n  contrib: add a hook that copies notes over rewrites\n\n Documentation/githooks.txt            |   36 +++++++\n builtin-commit.c                      |   43 ++++++++\n contrib/hooks/post-rewrite-copy-notes |   37 +++++++\n git-am.sh                             |   10 ++\n git-filter-branch.sh                  |   10 ++\n git-rebase--interactive.sh            |   41 +++++++-\n git-rebase.sh                         |    5 +\n t/t5407-post-rewrite-hook.sh          |  180 +++++++++++++++++++++++++++++++++\n 8 files changed, 361 insertions(+), 1 deletions(-)\n create mode 100644 contrib/hooks/post-rewrite-copy-notes\n create mode 100755 t/t5407-post-rewrite-hook.sh\n"},{"id":"134548","messageId":"508ef869b6ac0b7240d6608e24a791777686c610.1266164150.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266164150.git.trast@student.ethz.ch","subject":"[RFC PATCH 1/6] Documentation: document post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-14T16:17:06Z","receivedAt":"2010-02-14T16:17:06Z","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.216.g74d8e\n"},{"id":"134550","messageId":"f2a61f3f01c39bb20de020d2e7454417624e4d23.1266164150.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266164150.git.trast@student.ethz.ch","subject":"[RFC PATCH 2/6] commit --amend: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-14T16:17:07Z","receivedAt":"2010-02-14T16:17:07Z","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.216.g74d8e\n"},{"id":"134549","messageId":"0ad4b8c1a5377d697513cd8e49f64419cd8deef4.1266164150.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266164150.git.trast@student.ethz.ch","subject":"[RFC PATCH 3/6] filter-branch: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-14T16:17:08Z","receivedAt":"2010-02-14T16:17:08Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Signed-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n git-filter-branch.sh         |   10 ++++++++++\n t/t5407-post-rewrite-hook.sh |   20 ++++++++++++++++++++\n 2 files changed, 30 insertions(+), 0 deletions(-)\n\ndiff --git a/git-filter-branch.sh b/git-filter-branch.sh\nindex 88fb0f0..531cc8e 100755\n--- a/git-filter-branch.sh\n+++ b/git-filter-branch.sh\n@@ -358,6 +358,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 +488,12 @@ if [ \"$filter_tag_name\" ]; then\n fi\n \n cd ../..\n+\n+if test -x \"$GIT_DIR\"/hooks/post-rewrite &&\n+\ttest -s \"$workdir\"/../rewritten; then\n+\t\"$GIT_DIR\"/hooks/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 1020af9..ff23ebf 100755\n--- a/t/t5407-post-rewrite-hook.sh\n+++ b/t/t5407-post-rewrite-hook.sh\n@@ -49,4 +49,24 @@ test_expect_success 'git commit --amend --no-post-rewrite' '\n \ttest ! -f post-rewrite.data\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_done\n-- \n1.7.0.216.g74d8e\n"},{"id":"134552","messageId":"8493a921273c327c9da61d1e0ce887ce5e45dd5a.1266164150.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266164150.git.trast@student.ethz.ch","subject":"[RFC PATCH 4/6] rebase: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-14T16:17:09Z","receivedAt":"2010-02-14T16:17:09Z","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 ff23ebf..d6438a7 100755\n--- a/t/t5407-post-rewrite-hook.sh\n+++ b/t/t5407-post-rewrite-hook.sh\n@@ -69,4 +69,34 @@ test_expect_success 'git filter-branch (no-op)' '\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.216.g74d8e\n"},{"id":"134553","messageId":"b32eedebe7dceced1f1714d4a3ffd064fe74ef36.1266164150.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266164150.git.trast@student.ethz.ch","subject":"[RFC PATCH 5/6] rebase -i: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-14T16:17:10Z","receivedAt":"2010-02-14T16:17:10Z","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 d6438a7..d3ac469 100755\n--- a/t/t5407-post-rewrite-hook.sh\n+++ b/t/t5407-post-rewrite-hook.sh\n@@ -99,4 +99,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.216.g74d8e\n"},{"id":"134554","messageId":"fba55036b8c07f1ee10c4d8f7ed22045277aeb21.1266164150.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266164150.git.trast@student.ethz.ch","subject":"[RFC PATCH 6/6] contrib: add a hook that copies notes over rewrites","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-14T16:17:11Z","receivedAt":"2010-02-14T16:17:11Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"This hook can be used to copy your notes across rewrites.  You must\nalso choose across which type of rewrite you want to copy them, or\njust enable all of them by saying\n\n  git config hooks.rewriteCopyNotes all\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n contrib/hooks/post-rewrite-copy-notes |   37 +++++++++++++++++++++++++++++++++\n 1 files changed, 37 insertions(+), 0 deletions(-)\n create mode 100644 contrib/hooks/post-rewrite-copy-notes\n\ndiff --git a/contrib/hooks/post-rewrite-copy-notes b/contrib/hooks/post-rewrite-copy-notes\nnew file mode 100644\nindex 0000000..73d6a82\n--- /dev/null\n+++ b/contrib/hooks/post-rewrite-copy-notes\n@@ -0,0 +1,37 @@\n+#!/bin/sh\n+\n+# This hook copies over the git-notes annotation from the\n+# pre-rewritten commit to the post-rewritten one.  It only does so if\n+# the invoking command is listed in hooks.rewriteCopyNotes (which must\n+# be space separated).  If hooks.rewriteCopyNotesVerbose is set, it\n+# lists the commits for which 'git notes copy' was successful on\n+# stderr.\n+\n+type=\"$1\"\n+run=\n+\n+enabled=\"$(git config --get hooks.rewriteCopyNotes)\"\n+if test \"$enabled\" = all || test \"$enabled\" = true; then\n+    enabled=\"amend rebase filter-branch\"\n+fi\n+\n+for enabled_type in $enabled; do\n+    test \"$enabled_type\" = \"$type\" && run=t\n+done\n+\n+if test -z \"$run\"; then\n+    exit\n+done\n+\n+verbose=$(git config --get hooks.rewriteCopyNotesVerbose)\n+test \"$verbose\" = true && echo \"Copying notes:\" >2\n+end_msg=\"No commit had any notes.\"\n+\n+while read pre post extra; do\n+    git notes copy \"$pre\" \"$post\" 2>/dev/null &&\n+    end_msg=\"Done.\" &&\n+    test \"$verbose\" = true &&\n+    echo \"    $pre -> $post\" >2\n+done\n+\n+test \"$verbose\" = true && echo \"$end_msg\" >2\n-- \n1.7.0.216.g74d8e\n"},{"id":"134555","messageId":"201002141721.16165.trast@student.ethz.ch","threadId":"22652","inReplyTo":"fba55036b8c07f1ee10c4d8f7ed22045277aeb21.1266164150.git.trast@student.ethz.ch","subject":"Re: [RFC PATCH 6/6] contrib: add a hook that copies notes over rewrites","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-14T16:21:15Z","receivedAt":"2010-02-14T16:21:15Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"On Sunday 14 February 2010 17:17:11 Thomas Rast wrote:\n> +if test -z \"$run\"; then\n> +    exit\n> +done\n\nArgh, that's what I get for writing lots of tests except for the PoC\nhook.  This should of course say 'fi' instead of 'done'.  Sorry.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"134576","messageId":"0b241a335d71bdf56a07a712cb5506e1b1e1db6f.1266183704.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266164150.git.trast@student.ethz.ch","subject":"[PATCH] WIP: git notes copy --stdin","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-14T21:46:57Z","receivedAt":"2010-02-14T21:46:57Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"This implements a 'git notes copy --stdin' mode suitable to read the\ndata fed to the post-rewrite hook.\n---\n\nOk, so I changed my mind and decided to implement the \"make it\nbuilt-in and configurable\" way.  This helper should work.  I spent\nsome time trying to refactor cmd_notes() into something that can\nnicely handle commands that do not fit the normal scheme, but\neventually was too tired to continue and just crafted it in the\nexisting code in the right place.\n\nIf nobody beats me to it I'll pick this up later this week, but\ntomorrow will be too busy to even think about it.\n\n\n builtin-notes.c |   56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 56 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-notes.c b/builtin-notes.c\nindex 123ecad..3cd9e45 100644\n--- a/builtin-notes.c\n+++ b/builtin-notes.c\n@@ -278,6 +278,52 @@ 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+\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\tconst unsigned char *note;\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\tnote = get_note(t, from_obj);\n+\t\tif (!force) {\n+\t\t\tconst unsigned char *existing_note = get_note(t, to_obj);\n+\t\t\tif (existing_note) {\n+\t\t\t\terror(\"Cannot copy notes. Found existing notes for object\"\n+\t\t\t\t      \" %s. Use '-f' to overwrite existing notes\",\n+\t\t\t\t      sha1_to_hex(to_obj));\n+\t\t\t\treturn 1;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (note)\n+\t\t\tadd_note(t, to_obj, note, combine_notes_overwrite);\n+\t\telse\n+\t\t\tremove_note(t, to_obj);\n+\n+\t\tstrbuf_list_free(split);\n+\t}\n+\n+\tcommit_notes(t, \"Notes added by 'git notes copy'\");\n+\treturn 0;\n+}\n+\n int cmd_notes(int argc, const char **argv, const char *prefix)\n {\n \tstruct notes_tree *t;\n@@ -301,6 +347,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\", &force, \"read objects from stdin\"),\n \t\tOPT_END()\n \t};\n \n@@ -351,6 +398,15 @@ int cmd_notes(int argc, const char **argv, const char *prefix)\n \n \tif (copy) {\n \t\tconst char *from_ref;\n+\t\tif (stdin) {\n+\t\t\tif (argc > 2) {\n+\t\t\t\terror(\"too few parameters\");\n+\t\t\t\tusage_with_options(git_notes_usage, options);\n+\t\t\t} else {\n+\t\t\t\tretval = notes_copy_from_stdin(force);\n+\t\t\t\tgoto end;\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);\n-- \n1.7.0.224.g30f23.dirty\n"},{"id":"134587","messageId":"201002150225.52953.johan@herland.net","threadId":"22652","inReplyTo":"0b241a335d71bdf56a07a712cb5506e1b1e1db6f.1266183704.git.trast@student.ethz.ch","subject":"Re: [PATCH] WIP: git notes copy --stdin","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2010-02-15T01:25:52Z","receivedAt":"2010-02-15T01:25:52Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Sunday 14 February 2010, Thomas Rast wrote:\n> This implements a 'git notes copy --stdin' mode suitable to read the\n> data fed to the post-rewrite hook.\n> ---\n> \n> Ok, so I changed my mind and decided to implement the \"make it\n> built-in and configurable\" way.  This helper should work.  I spent\n> some time trying to refactor cmd_notes() into something that can\n> nicely handle commands that do not fit the normal scheme, but\n> eventually was too tired to continue and just crafted it in the\n> existing code in the right place.\n> \n> If nobody beats me to it I'll pick this up later this week, but\n> tomorrow will be too busy to even think about it.\n> \n> \n>  builtin-notes.c |   56\n> +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed,\n> 56 insertions(+), 0 deletions(-)\n> \n> diff --git a/builtin-notes.c b/builtin-notes.c\n> index 123ecad..3cd9e45 100644\n> --- a/builtin-notes.c\n> +++ b/builtin-notes.c\n> @@ -278,6 +278,52 @@ int commit_notes(struct notes_tree *t, const char\n> *msg) return 0;\n>  }\n> \n> +int notes_copy_from_stdin(int force)\n> +{\n> +\tstruct strbuf buf = STRBUF_INIT;\n> +\tstruct notes_tree *t;\n> +\n> +\tinit_notes(NULL, NULL, NULL, 0);\n> +\tt = &default_notes_tree;\n\nAs you say, cmd_notes() should be refactored, so that you don't have to \nduplicate all of this (and some of the below stuff).\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\tconst unsigned char *note;\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\tnote = get_note(t, from_obj);\n> +\t\tif (!force) {\n> +\t\t\tconst unsigned char *existing_note = get_note(t, to_obj);\n> +\t\t\tif (existing_note) {\n> +\t\t\t\terror(\"Cannot copy notes. Found existing notes for object\"\n> +\t\t\t\t      \" %s. Use '-f' to overwrite existing notes\",\n> +\t\t\t\t      sha1_to_hex(to_obj));\n> +\t\t\t\treturn 1;\n> +\t\t\t}\n> +\t\t}\n> +\n> +\t\tif (note)\n> +\t\t\tadd_note(t, to_obj, note, combine_notes_overwrite);\n> +\t\telse\n> +\t\t\tremove_note(t, to_obj);\n> +\n> +\t\tstrbuf_list_free(split);\n> +\t}\n> +\n> +\tcommit_notes(t, \"Notes added by 'git notes copy'\");\n\nWe (this goes for the existing commit_notes() call in cmd_notes() as well) \nshould either check the return value for commit_notes(), or (since it \ncurrently only returns 0) we should change commit_notes() to void instead of \nint. Actually, the correct solution is probably to \"return error()\" instead \nof \"die()\" inside commit_notes(), and then check the return value.\n\n> +\treturn 0;\n> +}\n> +\n>  int cmd_notes(int argc, const char **argv, const char *prefix)\n>  {\n>  \tstruct notes_tree *t;\n> @@ -301,6 +347,7 @@ int cmd_notes(int argc, const char **argv, const char\n> *prefix) OPT_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\", &force, \"read objects from stdin\"),\n\nWhy are you setting &force from \"--stdin\"?\n\n> @@ -351,6 +398,15 @@ int cmd_notes(int argc, const char **argv, const\n> char *prefix)\n> \n>  \tif (copy) {\n>  \t\tconst char *from_ref;\n> +\t\tif (stdin) {\n\nI don't think this is intended. Isn't stdin (the standard input FILE *) \n_always_ true?\n\n> +\t\t\tif (argc > 2) {\n> +\t\t\t\terror(\"too few parameters\");\n> +\t\t\t\tusage_with_options(git_notes_usage, options);\n> +\t\t\t} else {\n> +\t\t\t\tretval = notes_copy_from_stdin(force);\n> +\t\t\t\tgoto end;\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);\n\nThe patch needs to add code that barfs if \"--stdin\" is used with any \nsubcommand other than \"copy\".\n\nOf course, the patch also needs documentation and tests, but I guess you \nknew that... ;)\n\n\nI like the intent of this patch, although it needs more work before it's \nfinished.\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"134659","messageId":"4B79B035.2080604@kdbg.org","threadId":"22652","inReplyTo":"0ad4b8c1a5377d697513cd8e49f64419cd8deef4.1266164150.git.trast@student.ethz.ch","subject":"Re: [RFC PATCH 3/6] filter-branch: invoke post-rewrite hook","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2010-02-15T20:36:05Z","receivedAt":"2010-02-15T20:36:05Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Thomas Rast schrieb:\n> +if test -x \"$GIT_DIR\"/hooks/post-rewrite &&\n> +\ttest -s \"$workdir\"/../rewritten; then\n> +\t\"$GIT_DIR\"/hooks/post-rewrite filter-branch < \"$workdir\"/../rewritten\n> +fi\n\nThis sounds extra-strange. As if filter-branch is used 20 times a day. If \nthe intent is to carry notes over to new commits, then filter-branch \nshould grow a --notes-filter instruction, no? With an easily accessible \ncopy-everything mode like --tagname-filter=cat.\n\n-- Hannes\n"},{"id":"134792","messageId":"cover.1266361759.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266164150.git.trast@student.ethz.ch","subject":"[RFC PATCH v2 00/11] post-rewrite / automatic notes copying","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-16T23:25:56Z","receivedAt":"2010-02-16T23:25:56Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"I originally had two series, but to retain my sanity while shuffling\nfixups, I based the entire batch on a rebase of jh/notes onto current\nmaster.  The first four do not depend on jh/notes though, and are\nalmost unchanged from the first round.\n\nThe docs are probably sprinkled a bit strangely over the patches, not\nsure if it's worth a cleanup though (or I could just gather all the\ndocs in a final patch).  At least tests should be in the right place.\n\n\nBased on Hannes's comment:\n\nOn Monday 15 February 2010 21:36:05 Johannes Sixt wrote:\n> Thomas Rast schrieb:\n> > +if test -x \"$GIT_DIR\"/hooks/post-rewrite &&\n> > +\ttest -s \"$workdir\"/../rewritten; then\n> > +\t\"$GIT_DIR\"/hooks/post-rewrite filter-branch < \"$workdir\"/../rewritten\n> > +fi\n> \n> This sounds extra-strange. As if filter-branch is used 20 times a day. If \n> the intent is to carry notes over to new commits, then filter-branch \n> should grow a --notes-filter instruction, no? With an easily accessible \n> copy-everything mode like --tagname-filter=cat.\n\nI made filter-branch extra-tweakable, but tried to stay consistent\nwith the configuration of the rest of the series.  Maybe it's a bit\noverengineered now, dunno.\n\nOn Monday 15 February 2010 02:25:52 Johan Herland wrote:\n> On Sunday 14 February 2010, Thomas Rast wrote:\n> > \n> > I spent some time trying to refactor cmd_notes() into something\n> > that can nicely handle commands that do not fit the normal scheme,\n> > but eventually was too tired to continue and just crafted it in\n> > the existing code in the right place.\n[...]\n> As you say, cmd_notes() should be refactored, so that you don't have to \n> duplicate all of this (and some of the below stuff).\n\nActually I meant something different: it took me quite some time to\nfigure out what goes where in the flow of the current main() function,\nsince it is sprinkled with if(subcommand) -- for most of them, several\nsuch clauses.\n\nI guess it made sense at the time, because all commands more or less\nworked with the same arguments and mostly the same background logic,\njust in a slightly different way.  But I think it's quite unnatural\nfor an entirely-different command like 'copy --stdin'.\n\nBut maybe that's just me.\n\n> We (this goes for the existing commit_notes() call in cmd_notes() as well) \n> should either check the return value for commit_notes(), or (since it \n> currently only returns 0) we should change commit_notes() to void instead of \n> int. Actually, the correct solution is probably to \"return error()\" instead \n> of \"die()\" inside commit_notes(), and then check the return value.\n\nI'll leave that up to your judgement; I haven't looked into the API\nworkings much, so I'm not sure when it should fail how.\n\n[Thanks for the other comments on my embarrassing mistakes; I was\napparently rather too tired to continue programming.]\n\n\nThomas Rast (11):\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: clean up t3301\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\n Documentation/config.txt            |   17 +++\n Documentation/git-filter-branch.txt |   20 +++-\n Documentation/git-notes.txt         |   16 ++-\n Documentation/githooks.txt          |   40 +++++\n builtin-commit.c                    |   49 +++++++\n builtin-notes.c                     |  134 +++++++++++++++++-\n builtin.h                           |   15 ++\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                             |   21 +++\n notes.h                             |    9 ++\n t/t3301-notes.sh                    |  273 +++++++++++++++++++++++------------\n t/t3400-rebase.sh                   |   16 ++\n t/t3404-rebase-interactive.sh       |   10 ++\n t/t5407-post-rewrite-hook.sh        |  188 ++++++++++++++++++++++++\n t/t7003-filter-branch.sh            |   22 +++\n t/t7501-commit.sh                   |   11 ++\n 19 files changed, 843 insertions(+), 99 deletions(-)\n create mode 100755 t/t5407-post-rewrite-hook.sh\n"},{"id":"134793","messageId":"8ee58b83a10dcb302a33136768039729fbebae53.1266361759.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266361759.git.trast@student.ethz.ch","subject":"[RFC PATCH v2 01/11] Documentation: document post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-16T23:25:57Z","receivedAt":"2010-02-16T23:25:57Z","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.53.g5c2e6.dirty\n"},{"id":"134801","messageId":"297ce521ac8fa25ae00cbe8cf0a77d4fb9ee266d.1266361759.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266361759.git.trast@student.ethz.ch","subject":"[RFC PATCH v2 02/11] commit --amend: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-16T23:25:58Z","receivedAt":"2010-02-16T23:25:58Z","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.53.g5c2e6.dirty\n"},{"id":"134797","messageId":"844b90d4fdb9156431c740dc9b870dd0f8c9e21b.1266361759.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266361759.git.trast@student.ethz.ch","subject":"[RFC PATCH v2 03/11] rebase: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-16T23:25:59Z","receivedAt":"2010-02-16T23:25:59Z","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.53.g5c2e6.dirty\n"},{"id":"134798","messageId":"17b3a5c3d40c54bc9a045c0a09c421fc4bc988e0.1266361759.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266361759.git.trast@student.ethz.ch","subject":"[RFC PATCH v2 04/11] rebase -i: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-16T23:26:00Z","receivedAt":"2010-02-16T23:26:00Z","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.53.g5c2e6.dirty\n"},{"id":"134803","messageId":"2c7b0e51f7d299a481b62cbe4f728ff20e4651ab.1266361759.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266361759.git.trast@student.ethz.ch","subject":"[RFC PATCH v2 05/11] notes: clean up t3301","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-16T23:26:01Z","receivedAt":"2010-02-16T23:26:01Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"The existing tests handrolled test_commit, so use that instead.\n\nUnfortunately this changes all sha1's.  Since we're not supposed to\nunneccessarily rely on them anyway, introduce a small helper that\nfilters out the commit headers before comparing, and remove them from\nthe expected outputs.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n t/t3301-notes.sh |  141 ++++++++++++++++++------------------------------------\n 1 files changed, 46 insertions(+), 95 deletions(-)\n\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex 90178f9..3fec7ae 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -7,6 +7,15 @@ test_description='Test commit notes'\n \n . ./test-lib.sh\n \n+strip_header () {\n+\tsed -i '/^\\(commit\\|tree\\|parent\\) /d' \"$@\"\n+}\n+\n+strip_then_cmp () {\n+\tstrip_header \"$2\"\n+\ttest_cmp \"$@\"\n+}\n+\n cat > fake_editor.sh << \\EOF\n echo \"$MSG\" > \"$1\"\n echo \"$MSG\" >& 2\n@@ -20,14 +29,8 @@ test_expect_success 'cannot annotate non-existing HEAD' '\n '\n \n test_expect_success setup '\n-\t: > a1 &&\n-\tgit add a1 &&\n-\ttest_tick &&\n-\tgit commit -m 1st &&\n-\t: > a2 &&\n-\tgit add a2 &&\n-\ttest_tick &&\n-\tgit commit -m 2nd\n+\ttest_commit 1st &&\n+\ttest_commit 2nd\n '\n \n test_expect_success 'need valid notes ref' '\n@@ -92,7 +95,6 @@ test_expect_success 'can overwrite existing note with \"git notes add -f\"' '\n '\n \n cat > expect << EOF\n-commit 268048bfb8a1fb38e703baceb8ab235421bf80c5\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:14:13 2005 -0700\n \n@@ -105,21 +107,17 @@ EOF\n test_expect_success 'show notes' '\n \t! (git cat-file commit HEAD | grep b1) &&\n \tgit log -1 > output &&\n-\ttest_cmp expect output\n+\tstrip_then_cmp expect output\n '\n \n test_expect_success 'create multi-line notes (setup)' '\n-\t: > a3 &&\n-\tgit add a3 &&\n-\ttest_tick &&\n-\tgit commit -m 3rd &&\n+\ttest_commit 3rd &&\n \tMSG=\"b3\n c3c3c3c3\n d3d3d3\" git notes add\n '\n \n cat > expect-multiline << EOF\n-commit 1584215f1d29c65e99c6c6848626553fdd07fd75\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:15:13 2005 -0700\n \n@@ -136,19 +134,15 @@ cat expect >> expect-multiline\n \n test_expect_success 'show multi-line notes' '\n \tgit log -2 > output &&\n-\ttest_cmp expect-multiline output\n+\tstrip_then_cmp expect-multiline output\n '\n test_expect_success 'create -F notes (setup)' '\n-\t: > a4 &&\n-\tgit add a4 &&\n-\ttest_tick &&\n-\tgit commit -m 4th &&\n+\ttest_commit 4th &&\n \techo \"xyzzy\" > note5 &&\n \tgit notes add -F note5\n '\n \n cat > expect-F << EOF\n-commit 15023535574ded8b1a89052b32673f84cf9582b8\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:16:13 2005 -0700\n \n@@ -163,13 +157,10 @@ cat expect-multiline >> expect-F\n \n test_expect_success 'show -F notes' '\n \tgit log -3 > output &&\n-\ttest_cmp expect-F output\n+\tstrip_then_cmp expect-F output\n '\n \n cat >expect << EOF\n-commit 15023535574ded8b1a89052b32673f84cf9582b8\n-tree e070e3af51011e47b183c33adf9736736a525709\n-parent 1584215f1d29c65e99c6c6848626553fdd07fd75\n author A U Thor <author@example.com> 1112912173 -0700\n committer C O Mitter <committer@example.com> 1112912173 -0700\n \n@@ -177,7 +168,7 @@ committer C O Mitter <committer@example.com> 1112912173 -0700\n EOF\n test_expect_success 'git log --pretty=raw does not show notes' '\n \tgit log -1 --pretty=raw >output &&\n-\ttest_cmp expect output\n+\tstrip_then_cmp expect output\n '\n \n cat >>expect <<EOF\n@@ -187,7 +178,7 @@ Notes:\n EOF\n test_expect_success 'git log --show-notes' '\n \tgit log -1 --pretty=raw --show-notes >output &&\n-\ttest_cmp expect output\n+\tstrip_then_cmp expect output\n '\n \n test_expect_success 'git log --no-notes' '\n@@ -220,10 +211,7 @@ do\n done\n \n test_expect_success 'create -m notes (setup)' '\n-\t: > a5 &&\n-\tgit add a5 &&\n-\ttest_tick &&\n-\tgit commit -m 5th &&\n+\ttest_commit 5th &&\n \tgit notes add -m spam -m \"foo\n bar\n baz\"\n@@ -231,7 +219,6 @@ baz\"\n \n whitespace=\"    \"\n cat > expect-m << EOF\n-commit bd1753200303d0a0344be813e504253b3d98e74d\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:17:13 2005 -0700\n \n@@ -250,7 +237,7 @@ cat expect-F >> expect-m\n \n test_expect_success 'show -m notes' '\n \tgit log -4 > output &&\n-\ttest_cmp expect-m output\n+\tstrip_then_cmp expect-m output\n '\n \n test_expect_success 'remove note with add -f -F /dev/null (setup)' '\n@@ -258,7 +245,6 @@ test_expect_success 'remove note with add -f -F /dev/null (setup)' '\n '\n \n cat > expect-rm-F << EOF\n-commit bd1753200303d0a0344be813e504253b3d98e74d\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:17:13 2005 -0700\n \n@@ -270,7 +256,7 @@ cat expect-F >> expect-rm-F\n \n test_expect_success 'verify note removal with -F /dev/null' '\n \tgit log -4 > output &&\n-\ttest_cmp expect-rm-F output &&\n+\tstrip_then_cmp expect-rm-F output &&\n \t! git notes show\n '\n \n@@ -280,7 +266,7 @@ test_expect_success 'do not create empty note with -m \"\" (setup)' '\n \n test_expect_success 'verify non-creation of note with -m \"\"' '\n \tgit log -4 > output &&\n-\ttest_cmp expect-rm-F output &&\n+\tstrip_then_cmp expect-rm-F output &&\n \t! git notes show\n '\n \n@@ -301,7 +287,7 @@ test_expect_success 'create note with combination of -m and -F' '\n \techo \"zyxxy\" > note_b &&\n \tgit notes add -m \"foo\" -F note_a -m \"bar\" -F note_b -m \"baz\" &&\n \tgit notes show > output &&\n-\ttest_cmp expect-combine_m_and_F output\n+\tstrip_then_cmp expect-combine_m_and_F output\n '\n \n test_expect_success 'remove note with \"git notes remove\" (setup)' '\n@@ -310,13 +296,11 @@ test_expect_success 'remove note with \"git notes remove\" (setup)' '\n '\n \n cat > expect-rm-remove << EOF\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-commit 15023535574ded8b1a89052b32673f84cf9582b8\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:16:13 2005 -0700\n \n@@ -328,13 +312,13 @@ cat expect-multiline >> expect-rm-remove\n \n test_expect_success 'verify note removal with \"git notes remove\"' '\n \tgit log -4 > output &&\n-\ttest_cmp expect-rm-remove output &&\n+\tstrip_then_cmp expect-rm-remove output &&\n \t! git notes show HEAD^\n '\n \n cat > expect << EOF\n-c18dc024e14f08d18d14eea0d747ff692d66d6a3 1584215f1d29c65e99c6c6848626553fdd07fd75\n-c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 268048bfb8a1fb38e703baceb8ab235421bf80c5\n+c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 7a4ca6ee52a974a66cbaa78e33214535dff1d691\n+c18dc024e14f08d18d14eea0d747ff692d66d6a3 d07d62e5208f22eb5695e7eb47667dc8b9860290\n EOF\n \n test_expect_success 'list notes with \"git notes list\"' '\n@@ -401,15 +385,11 @@ test_expect_success 'appending empty string to non-existing note does not create\n '\n \n test_expect_success 'create other note on a different notes ref (setup)' '\n-\t: > a6 &&\n-\tgit add a6 &&\n-\ttest_tick &&\n-\tgit commit -m 6th &&\n+\ttest_commit 6th &&\n \tGIT_NOTES_REF=\"refs/notes/other\" git notes add -m \"other note\"\n '\n \n cat > expect-other << EOF\n-commit 387a89921c73d7ed72cd94d179c1c7048ca47756\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:18:13 2005 -0700\n \n@@ -420,7 +400,6 @@ Notes:\n EOF\n \n cat > expect-not-other << EOF\n-commit 387a89921c73d7ed72cd94d179c1c7048ca47756\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:18:13 2005 -0700\n \n@@ -429,23 +408,23 @@ EOF\n \n test_expect_success 'Do not show note on other ref by default' '\n \tgit log -1 > output &&\n-\ttest_cmp expect-not-other output\n+\tstrip_then_cmp expect-not-other output\n '\n \n test_expect_success 'Do show note when ref is given in GIT_NOTES_REF' '\n \tGIT_NOTES_REF=\"refs/notes/other\" git log -1 > output &&\n-\ttest_cmp expect-other output\n+\tstrip_then_cmp expect-other output\n '\n \n test_expect_success 'Do show note when ref is given in core.notesRef config' '\n \tgit config core.notesRef \"refs/notes/other\" &&\n \tgit log -1 > output &&\n-\ttest_cmp expect-other output\n+\tstrip_then_cmp expect-other output\n '\n \n test_expect_success 'Do not show note when core.notesRef is overridden' '\n \tGIT_NOTES_REF=\"refs/notes/wrong\" git log -1 > output &&\n-\ttest_cmp expect-not-other output\n+\tstrip_then_cmp expect-not-other output\n '\n \n test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '\n@@ -466,7 +445,6 @@ test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '\n '\n \n cat > expect << EOF\n-commit 2ede89468182a62d0bde2583c736089bcf7d7e92\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:19:13 2005 -0700\n \n@@ -477,27 +455,20 @@ Notes:\n EOF\n \n test_expect_success 'create note from other note with \"git notes add -C\"' '\n-\t: > a7 &&\n-\tgit add a7 &&\n-\ttest_tick &&\n-\tgit commit -m 7th &&\n+\ttest_commit 7th &&\n \tgit notes add -C $(git notes list HEAD^) &&\n \tgit log -1 > actual &&\n-\ttest_cmp expect actual &&\n+\tstrip_then_cmp expect actual &&\n \ttest \"$(git notes list HEAD)\" = \"$(git notes list HEAD^)\"\n '\n \n test_expect_success 'create note from non-existing note with \"git notes add -C\" fails' '\n-\t: > a8 &&\n-\tgit add a8 &&\n-\ttest_tick &&\n-\tgit commit -m 8th &&\n+\ttest_commit 8th &&\n \ttest_must_fail git notes add -C deadbeef &&\n \ttest_must_fail git notes list HEAD\n '\n \n cat > expect << EOF\n-commit 016e982bad97eacdbda0fcbd7ce5b0ba87c81f1b\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:21:13 2005 -0700\n \n@@ -508,26 +479,19 @@ Notes:\n EOF\n \n test_expect_success 'create note from other note with \"git notes add -c\"' '\n-\t: > a9 &&\n-\tgit add a9 &&\n-\ttest_tick &&\n-\tgit commit -m 9th &&\n+\ttest_commit 9th &&\n \tMSG=\"yet another note\" git notes add -c $(git notes list HEAD^^) &&\n \tgit log -1 > actual &&\n-\ttest_cmp expect actual\n+\tstrip_then_cmp expect actual\n '\n \n test_expect_success 'create note from non-existing note with \"git notes add -c\" fails' '\n-\t: > a10 &&\n-\tgit add a10 &&\n-\ttest_tick &&\n-\tgit commit -m 10th &&\n+\ttest_commit 10th &&\n \ttest_must_fail MSG=\"yet another note\" git notes add -c deadbeef &&\n \ttest_must_fail git notes list HEAD\n '\n \n cat > expect << EOF\n-commit 016e982bad97eacdbda0fcbd7ce5b0ba87c81f1b\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:21:13 2005 -0700\n \n@@ -542,11 +506,10 @@ EOF\n test_expect_success 'append to note from other note with \"git notes append -C\"' '\n \tgit notes append -C $(git notes list HEAD^) HEAD^ &&\n \tgit log -1 HEAD^ > actual &&\n-\ttest_cmp expect actual\n+\tstrip_then_cmp expect actual\n '\n \n cat > expect << EOF\n-commit ffed603236bfa3891c49644257a83598afe8ae5a\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:22:13 2005 -0700\n \n@@ -559,11 +522,10 @@ EOF\n test_expect_success 'create note from other note with \"git notes append -c\"' '\n \tMSG=\"other note\" git notes append -c $(git notes list HEAD^) &&\n \tgit log -1 > actual &&\n-\ttest_cmp expect actual\n+\tstrip_then_cmp expect actual\n '\n \n cat > expect << EOF\n-commit ffed603236bfa3891c49644257a83598afe8ae5a\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:22:13 2005 -0700\n \n@@ -578,11 +540,10 @@ EOF\n test_expect_success 'append to note from other note with \"git notes append -c\"' '\n \tMSG=\"yet another note\" git notes append -c $(git notes list HEAD) &&\n \tgit log -1 > actual &&\n-\ttest_cmp expect actual\n+\tstrip_then_cmp expect actual\n '\n \n cat > expect << EOF\n-commit 6352c5e33dbcab725fe0579be16aa2ba8eb369be\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:23:13 2005 -0700\n \n@@ -595,25 +556,21 @@ $whitespace\n EOF\n \n test_expect_success 'copy note with \"git notes copy\"' '\n-\t: > a11 &&\n-\tgit add a11 &&\n-\ttest_tick &&\n-\tgit commit -m 11th &&\n+\ttest_commit 11th &&\n \tgit notes copy HEAD^ HEAD &&\n \tgit log -1 > actual &&\n-\ttest_cmp expect actual &&\n+\tstrip_then_cmp expect actual &&\n \ttest \"$(git notes list HEAD)\" = \"$(git notes list HEAD^)\"\n '\n \n test_expect_success 'prevent overwrite with \"git notes copy\"' '\n \ttest_must_fail git notes copy HEAD~2 HEAD &&\n \tgit log -1 > actual &&\n-\ttest_cmp expect actual &&\n+\tstrip_then_cmp expect actual &&\n \ttest \"$(git notes list HEAD)\" = \"$(git notes list HEAD^)\"\n '\n \n cat > expect << EOF\n-commit 6352c5e33dbcab725fe0579be16aa2ba8eb369be\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:23:13 2005 -0700\n \n@@ -628,19 +585,13 @@ EOF\n test_expect_success 'allow overwrite with \"git notes copy -f\"' '\n \tgit notes copy -f HEAD~2 HEAD &&\n \tgit log -1 > actual &&\n-\ttest_cmp expect actual &&\n+\tstrip_then_cmp expect actual &&\n \ttest \"$(git notes list HEAD)\" = \"$(git notes list HEAD~2)\"\n '\n \n test_expect_success 'cannot copy note from object without notes' '\n-\t: > a12 &&\n-\tgit add a12 &&\n-\ttest_tick &&\n-\tgit commit -m 12th &&\n-\t: > a13 &&\n-\tgit add a13 &&\n-\ttest_tick &&\n-\tgit commit -m 13th &&\n+\ttest_commit 12th &&\n+\ttest_commit 13th &&\n \ttest_must_fail git notes copy HEAD^ HEAD\n '\n \n-- \n1.7.0.53.g5c2e6.dirty\n"},{"id":"134799","messageId":"d8649cbda00586a9dfbd5085bb7338a5824134b1.1266361759.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266361759.git.trast@student.ethz.ch","subject":"[RFC PATCH v2 06/11] notes: implement 'git notes copy --stdin'","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-16T23:26:02Z","receivedAt":"2010-02-16T23:26:02Z","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..30420d1 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+\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+\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,22 @@ 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\tretval = notes_copy_from_stdin(force);\n+\t\t\t\tgoto end;\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 3ba3e6d..a5ff723 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -1030,3 +1030,24 @@ void format_note(struct notes_tree *t, const unsigned char *object_sha1,\n \n \tfree(msg);\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 bad03cc..b7547bf 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 3fec7ae..9396080 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -595,4 +595,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:\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:\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.53.g5c2e6.dirty\n"},{"id":"134795","messageId":"b8d8e699a7067f0bb095f4df73966fc61ffeb6c6.1266361759.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266361759.git.trast@student.ethz.ch","subject":"[RFC PATCH v2 07/11] notes: implement helpers needed for note copying during rewrite","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-16T23:26:03Z","receivedAt":"2010-02-16T23:26:03Z","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    |   13 ++++++\n Documentation/git-notes.txt |    4 ++\n Documentation/githooks.txt  |    4 ++\n builtin-notes.c             |   89 +++++++++++++++++++++++++++++++++++---\n builtin.h                   |   15 ++++++\n t/t3301-notes.sh            |  100 +++++++++++++++++++++++++++++++++++++++++++\n 6 files changed, 219 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 4c36aa9..403f5cf 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1300,6 +1300,19 @@ mergetool.keepTemporaries::\n mergetool.prompt::\n \tPrompt before each invocation of the merge resolution program.\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 `false`.\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 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 30420d1..116dbf6 100644\n--- a/builtin-notes.c\n+++ b/builtin-notes.c\n@@ -278,15 +278,81 @@ 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}\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 = 0;\n+\tc->combine = combine_notes_concatenate;\n+\tgit_config(notes_rewrite_config, c);\n+\tif (!c->enabled) {\n+\t\tfree(c);\n+\t\treturn NULL;\n+\t}\n+\tinit_notes(NULL, NULL, NULL, 0);\n+\tc->tree = &default_notes_tree;\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+\treturn copy_note(c->tree, from_obj, to_obj, 1, c->combine);\n+}\n+\n+void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c)\n+{\n+\tcommit_notes(c->tree, \"Notes added by 'git notes copy'\");\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 +369,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@@ -330,6 +400,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 +413,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 +463,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 +474,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\tretval = notes_copy_from_stdin(force);\n+\t\t\t\tretval = notes_copy_from_stdin(force, rewrite_cmd);\n \t\t\t\tgoto end;\n \t\t\t}\n \t\t}\ndiff --git a/builtin.h b/builtin.h\nindex cdf9847..f71f44f 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -20,6 +20,21 @@ 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 *tree;\n+\tconst char *cmd;\n+\tint enabled;\n+\tcombine_notes_fn *combine;\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 9396080..c91a714 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -627,4 +627,104 @@ 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+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\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+\tstrip_then_cmp expect output\n+'\n+\n+cat > expect << EOF\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes:\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:26:13 2005 -0700\n+\n+    14th\n+\n+Notes:\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.rewrite.foo true &&\n+\tgit config notes.rewriteMode overwrite &&\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+\tstrip_then_cmp expect output\n+'\n+\n+cat > expect << EOF\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes:\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+\tstrip_then_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+\tstrip_then_cmp expect output\n+'\n+\n+cat > expect << EOF\n+Author: A U Thor <author@example.com>\n+Date:   Thu Apr 7 15:27:13 2005 -0700\n+\n+    15th\n+\n+Notes:\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+\tstrip_then_cmp expect output\n+'\n+\n test_done\n-- \n1.7.0.53.g5c2e6.dirty\n"},{"id":"134794","messageId":"621ce5ad0290d5df0022a7929070a2e0a72c7c22.1266361759.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266361759.git.trast@student.ethz.ch","subject":"[RFC PATCH v2 08/11] rebase: support automatic notes copying","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-16T23:26:04Z","receivedAt":"2010-02-16T23:26:04Z","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             |   16 ++++++++++++++++\n t/t3404-rebase-interactive.sh |   10 ++++++++++\n 5 files changed, 35 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..4608196 100755\n--- a/t/t3400-rebase.sh\n+++ b/t/t3400-rebase.sh\n@@ -151,4 +151,20 @@ 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+\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..5b5104c 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -553,4 +553,14 @@ 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+\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.53.g5c2e6.dirty\n"},{"id":"134802","messageId":"daa04eff7f7ac4a70e580e3432188a8e3aa754fa.1266361759.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266361759.git.trast@student.ethz.ch","subject":"[RFC PATCH v2 09/11] commit --amend: copy notes to the new commit","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-16T23:26:05Z","receivedAt":"2010-02-16T23:26:05Z","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 |   11 +++++++++++\n 2 files changed, 17 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..93d3c81 100755\n--- a/t/t7501-commit.sh\n+++ b/t/t7501-commit.sh\n@@ -425,4 +425,15 @@ 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+\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.53.g5c2e6.dirty\n"},{"id":"134796","messageId":"c1498be72baf88ffd78c3a0daa06aa7d8000e244.1266361759.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266361759.git.trast@student.ethz.ch","subject":"[RFC PATCH v2 10/11] filter-branch: invoke post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-16T23:26:06Z","receivedAt":"2010-02-16T23:26:06Z","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.53.g5c2e6.dirty\n"},{"id":"134800","messageId":"a1bdef42de198dec4ec59c0d2b8b67e8656192d1.1266361759.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"cover.1266361759.git.trast@student.ethz.ch","subject":"[RFC PATCH v2 11/11] filter-branch: learn how to filter notes","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-16T23:26:07Z","receivedAt":"2010-02-16T23:26:07Z","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            |   12 ++++++++----\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, 64 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 403f5cf..2dfab6d 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1301,10 +1301,10 @@ mergetool.prompt::\n \tPrompt before each invocation of the merge resolution program.\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 `false`.\n+\tWhen rewriting commits with <command> (currently `amend`,\n+\t`rebase` or `filter-branch`) and this variable is set to\n+\t`true`, git automatically copies your notes from the original\n+\tto the rewritten commit.  Defaults to `false`.\n \n notes.rewriteMode::\n \tWhen copying notes during a rewrite (see the\n@@ -1312,6 +1312,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 pack.window::\n \tThe size of the window used by linkgit:git-pack-objects[1] when no\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.53.g5c2e6.dirty\n"},{"id":"134812","messageId":"7vd40420yb.fsf@alter.siamese.dyndns.org","threadId":"22652","inReplyTo":"2c7b0e51f7d299a481b62cbe4f728ff20e4651ab.1266361759.git.trast@student.ethz.ch","subject":"Re: [RFC PATCH v2 05/11] notes: clean up t3301","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-16T23:52:12Z","receivedAt":"2010-02-16T23:52:12Z","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> The existing tests handrolled test_commit, so use that instead.\n>\n> Unfortunately this changes all sha1's.  Since we're not supposed to\n> unneccessarily rely on them anyway, introduce a small helper that\n> filters out the commit headers before comparing, and remove them from\n> the expected outputs.\n>\n> Signed-off-by: Thomas Rast <trast@student.ethz.ch>\n> ---\n>  t/t3301-notes.sh |  141 ++++++++++++++++++------------------------------------\n>  1 files changed, 46 insertions(+), 95 deletions(-)\n>\n> diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\n> index 90178f9..3fec7ae 100755\n> --- a/t/t3301-notes.sh\n> +++ b/t/t3301-notes.sh\n> @@ -7,6 +7,15 @@ test_description='Test commit notes'\n>  \n>  . ./test-lib.sh\n>  \n> +strip_header () {\n> +\tsed -i '/^\\(commit\\|tree\\|parent\\) /d' \"$@\"\n> +}\n\nI do not think -i is portable.  Neither the use of ERE elements by quoting\nthem with backslash (which is a non-POSIX GNU extension IIRC).\n\nYou use this only for one side of test_cmp, so you should be able to do\nsomething like:\n\n\tstrip_then_cmp () {\n        \tsed -e \"/^[ctp][a-z]* $x40$/d\" \"$2\" |\n                test_cmp \"$1\" -\n\t}\n\nBut because you are using test_commit to freeze the time and get stable\noutput, and because you still have something like this anyway:\n\n>  cat > expect << EOF\n> -c18dc024e14f08d18d14eea0d747ff692d66d6a3 1584215f1d29c65e99c6c6848626553fdd07fd75\n> -c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 268048bfb8a1fb38e703baceb8ab235421bf80c5\n> +c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 7a4ca6ee52a974a66cbaa78e33214535dff1d691\n> +c18dc024e14f08d18d14eea0d747ff692d66d6a3 d07d62e5208f22eb5695e7eb47667dc8b9860290\n>  EOF\n\nit might be easier to do without such a niceness.\n"},{"id":"134814","messageId":"7v635w20o5.fsf@alter.siamese.dyndns.org","threadId":"22652","inReplyTo":"b8d8e699a7067f0bb095f4df73966fc61ffeb6c6.1266361759.git.trast@student.ethz.ch","subject":"Re: [RFC PATCH v2 07/11] notes: implement helpers needed for note copying during rewrite","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-16T23:58:18Z","receivedAt":"2010-02-16T23:58:18Z","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> Implement helper functions to load the rewriting config, and to\n> actually copy the notes.  Also document the config.\n\nI find it somewhat unnerving that this series is a bit overengineered to\nhave fancier and facier handling of notes, while at the same time still\nsticking to the \"only one default notes tree\" mode of operation.  Are you\ndigging a deep hole that you will find hard to get out of when you finally\nneed to support copying/rewriting notes from multiple trees at the same\ntime?\n"},{"id":"134816","messageId":"7vd404zq88.fsf@alter.siamese.dyndns.org","threadId":"22652","inReplyTo":"8ee58b83a10dcb302a33136768039729fbebae53.1266361759.git.trast@student.ethz.ch","subject":"Re: [RFC PATCH v2 01/11] Documentation: document post-rewrite hook","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-16T23:59:51Z","receivedAt":"2010-02-16T23:59: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> +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\nSimilar to filter-branch being silent on dropped commits, I presume\n\"rebase -i\" will stay silent if a pick is removed, or rebase --skip is\ngiven.\n\nI wondered if squash/fixup case wants to distinguish the \"primary\" commit\nand \"follow-up\" fixup commits (not a complaint, but thinking-aloud), but I\nthink the rebase command itself and this hook have the same amount of\ninformation needed to guess which one is the primary and which ones are\nthe follow ups, so it probably is Ok.\n\nIs the order of input lines guaranteed in some way?  For example, if I run\nrebase to make A B C into A' and B+C, do we get these three lines:\n\n\tA -> A'\n        B -> B+C\n        C -> B+C\n\nin some known order?  If so, should we document that order?\n"},{"id":"134819","messageId":"201002170109.25211.trast@student.ethz.ch","threadId":"22652","inReplyTo":"7v635w20o5.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC PATCH v2 07/11] notes: implement helpers needed for note copying during rewrite","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-17T00:09:24Z","receivedAt":"2010-02-17T00:09:24Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"On Wednesday 17 February 2010 00:58:18 Junio C Hamano wrote:\n> Thomas Rast <trast@student.ethz.ch> writes:\n> \n> > Implement helper functions to load the rewriting config, and to\n> > actually copy the notes.  Also document the config.\n> \n> I find it somewhat unnerving that this series is a bit overengineered to\n> have fancier and facier handling of notes, while at the same time still\n> sticking to the \"only one default notes tree\" mode of operation.  Are you\n> digging a deep hole that you will find hard to get out of when you finally\n> need to support copying/rewriting notes from multiple trees at the same\n> time?\n\nHmm, very good point.\n\nI believe the code in this patch is fairly well-prepared to deal with\nit.  It would merely need to learn to store a list of trees in the\nnotes_rewrite_cfg, provided that it's ok to load them all\nsimultaneously.\n\nHowever, I haven't really thought about having several notes trees.\nIs this automatic copying the only operation that needs this feature,\nor were you thinking of others too?  Is it enough to, say, introduce a\nway of configuring it at the branch level, or declaring that we\nrewrite everything in refs/notes, or some such?\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"134820","messageId":"7v7hqcww7z.fsf@alter.siamese.dyndns.org","threadId":"22652","inReplyTo":"201002170109.25211.trast@student.ethz.ch","subject":"Re: [RFC PATCH v2 07/11] notes: implement helpers needed for note copying during rewrite","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-17T00:18:40Z","receivedAt":"2010-02-17T00:18: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> I believe the code in this patch is fairly well-prepared to deal with\n> it.  It would merely need to learn to store a list of trees in the\n> notes_rewrite_cfg, provided that it's ok to load them all\n> simultaneously.\n\nI was mentally handwaving to myself that all will be well in the end ;-)\nbut thought that it would never hurt to ask if you have thought things\nthrough.\n\n> However, I haven't really thought about having several notes trees.\n> Is this automatic copying the only operation that needs this feature,\n> or were you thinking of others too?\n\nI was envisioning that most of the time people would want to read from\n(and probably concatenate, or output with the origin information tagged in)\nmultiple notes but want to create into a single one.  But updating for\nrewritten commits fundamentally need to happen in all of these multiple\nnotes trees they would want to read from.\n\nFor example, if I have amlog notes to record the message-id of the\noriginal message I ran \"am\" to queue people's patches, I would want to\ncarry that even after I make typofixes in the commit log message or in the\ncode using \"commit --amend\".  If you have buildlog notes to record the\noutput from continuously running build-test-bot, you probably do not want\nthese notes to get carried forward when you rebase a branch.\n\nIn \"log --show-notes=amlog --show-notes=buildlog\" output, we will see\nnotes from both amlog and buildlog in the original history, but only the\nnotes from amlog will be shown in the rebased history.\n"},{"id":"134821","messageId":"201002170118.53639.trast@student.ethz.ch","threadId":"22652","inReplyTo":"7vd404zq88.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC PATCH v2 01/11] Documentation: document post-rewrite hook","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-17T00:18:53Z","receivedAt":"2010-02-17T00:18:53Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"On Wednesday 17 February 2010 00:59:51 Junio C Hamano wrote:\n> Thomas Rast <trast@student.ethz.ch> writes:\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> Similar to filter-branch being silent on dropped commits, I presume\n> \"rebase -i\" will stay silent if a pick is removed, or rebase --skip is\n> given.\n\nIndeed.  Due to the way that rebase works, it would be a hassle to\nreport '00..00' (or so) for dropped commits, so I'm not too keen on\ngoing there.  (My intended use-cases don't require it.  Do you have\none that does?)  Documenting this absence wouldn't hurt though.\n\n> I wondered if squash/fixup case wants to distinguish the \"primary\" commit\n> and \"follow-up\" fixup commits (not a complaint, but thinking-aloud), but I\n> think the rebase command itself and this hook have the same amount of\n> information needed to guess which one is the primary and which ones are\n> the follow ups, so it probably is Ok.\n\nThat's what I put in the <extra-info> for :-)\n\n> Is the order of input lines guaranteed in some way?  For example, if I run\n> rebase to make A B C into A' and B+C, do we get these three lines:\n> \n> \tA -> A'\n>         B -> B+C\n>         C -> B+C\n> \n> in some known order?  If so, should we document that order?\n\nCurrently they're in the order that they were rewritten, but what\nwould the hook learn from that?\n\nI left it unspecified because I wasn't sure we would always have the\nlist in a specific order (e.g., when reconstructed from a hash map of\nrewritings, it's not) but then, keeping the list ordered isn't that\nexpensive either...\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"134823","messageId":"7v4olgvh5y.fsf@alter.siamese.dyndns.org","threadId":"22652","inReplyTo":"201002170118.53639.trast@student.ethz.ch","subject":"Re: [RFC PATCH v2 01/11] Documentation: document post-rewrite hook","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-17T00:29:13Z","receivedAt":"2010-02-17T00:29:13Z","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 Wednesday 17 February 2010 00:59:51 Junio C Hamano wrote:\n>> Thomas Rast <trast@student.ethz.ch> writes:\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>> Similar to filter-branch being silent on dropped commits, I presume\n>> \"rebase -i\" will stay silent if a pick is removed, or rebase --skip is\n>> given.\n>\n> Indeed.  Due to the way that rebase works, it would be a hassle to\n> report '00..00' (or so) for dropped commits, so I'm not too keen on\n> going there.  (My intended use-cases don't require it.  Do you have\n> one that does?)  Documenting this absence wouldn't hurt though.\n\nI was solely interested in the documentation at this point.  I don't see a\nreason to report \"old -> nul\"; somebody else may, but I don't.\n\n>> Is the order of input lines guaranteed in some way?  For example, if I run\n>> rebase to make A B C into A' and B+C, do we get these three lines:\n>> \n>> \tA -> A'\n>>         B -> B+C\n>>         C -> B+C\n>> \n>> in some known order?  If so, should we document that order?\n>\n> Currently they're in the order that they were rewritten, but what\n> would the hook learn from that?\n\nFor example, a user may say \"I _always_ make a big commit that describes\neverything at the beginning, and then follow-ups are irrelevant\", in which\ncase the user may want to say \"notes for B carries to B+C but discard\nanything attached to C\" in _his_ hook.\n\nAnother example.  A build-bot may attach \"builds fine\" note to C but B may\nhave \"failed to build\" note without the fix-up in C.  B+C will want to\nhave the \"builds fine\" note from C (you would probably also need to verify\nthat C^{tree} == (B+C)^{tree} before moving the note, but the point is you\ndo not even want to consider moving it from B to B+C).\n\nThese become possible if the output at least guarantees topological\nordering for things that are squashed into one.\n\nBut again I am just thinking aloud.  I don't know how much benefit will\nsuch a guarantee give us in practice.\n"},{"id":"134887","messageId":"4B7C4A9E.9030906@kdbg.org","threadId":"22652","inReplyTo":"a1bdef42de198dec4ec59c0d2b8b67e8656192d1.1266361759.git.trast@student.ethz.ch","subject":"Re: [RFC PATCH v2 11/11] filter-branch: learn how to filter notes","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2010-02-17T19:59:26Z","receivedAt":"2010-02-17T19:59:26Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Thomas Rast schrieb:\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\nI think that this form of --notes-filter is not flexible enough.\n\n1. There is no possibility for the filter to decide whether the note \nshould be attached to the rewritten commit or not.\n\n2. The implementation is not concerned about different notes namespaces \n(IIUC).\n\n3. There is probably more.\n\nOne use-case that comes to mind is to move notes from one particular \nnamespace to a different one (and I mean move, not just copy). Don't know \nif 'git notes' itself has such a feature.\n\nI am not a 'git notes' user at this time, nor do I know anything about the \nimplementation, nor did I follow any discussions about notes, hence, I'm \nnot in the position to judge what --notes-filter could be useful for. I \nknow I talked you into implementing it, but that is just because I dislike \nthat filter-branch calls a post-rewrite hook (that just doesn't sound \nright to me, given the one-shot nature of filter-branch), and your \nintention obviously was to transfer notes.\n\n-- Hannes\n"},{"id":"134898","messageId":"201002180006.36749.trast@student.ethz.ch","threadId":"22652","inReplyTo":"4B7C4A9E.9030906@kdbg.org","subject":"Re: [RFC PATCH v2 11/11] filter-branch: learn how to filter notes","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-17T23:06:32Z","receivedAt":"2010-02-17T23:06:32Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"On Wednesday 17 February 2010 20:59:26 Johannes Sixt wrote:\n> I think that this form of --notes-filter is not flexible enough.\n> \n> 1. There is no possibility for the filter to decide whether the note \n> should be attached to the rewritten commit or not.\n\nThat's not 100% correct, but it's not your fault for not knowing: if\nyou add an empty note, that's the same as removing it.\n\n> 2. The implementation is not concerned about different notes namespaces \n> (IIUC).\n> \n> 3. There is probably more.\n> \n> One use-case that comes to mind is to move notes from one particular \n> namespace to a different one (and I mean move, not just copy). Don't know \n> if 'git notes' itself has such a feature.\n> \n> I am not a 'git notes' user at this time, nor do I know anything about the \n> implementation, nor did I follow any discussions about notes, hence, I'm \n> not in the position to judge what --notes-filter could be useful\n> for.\n\nI'm currently leaning towards declaring this the realm of\n--post-rewrite, not --notes-filter.  I'm not saying that the former is\nthe perfect / most convenient format in which such a hook could be\nspecified, but I believe its input should just be pre and post rewrite\nsha1.  Using it to move notes, or whatever, should be up to the user.\n\nThat's not to say that there shouldn't be some convenient interface to\nmanage several notes trees, which I'm not yet clear on.\n\n> I know I talked you into implementing it, but that is just because I dislike \n> that filter-branch calls a post-rewrite hook (that just doesn't sound \n> right to me, given the one-shot nature of filter-branch), and your \n> intention obviously was to transfer notes.\n\nNo (as I said in the original series leader) my original motivation\nwas to have a hook that can record the pre-write sha1 in a note on the\nrewritten commit.  I noticed that it could also be used to transfer\nthe notes to the rewritten commit, which was part of the reason why I\nimplemented it.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"135159","messageId":"8c3f1d66d306747d777f04621f1d43d7e07ddc6a.1266677264.git.trast@student.ethz.ch","threadId":"22652","inReplyTo":"7v7hqcww7z.fsf@alter.siamese.dyndns.org","subject":"[WIP/RFC PATCH] Support showing notes from more than one notes tree","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-20T14:58:47Z","receivedAt":"2010-02-20T14:58:47Z","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---\n\nI'm running out of git time today, so I'm floating this weatherbaloon\nto demonstrate how I would like to get around the limitations\ndiscussed by Junio:\n\n> I was envisioning that most of the time people would want to read from\n> (and probably concatenate, or output with the origin information tagged in)\n> multiple notes but want to create into a single one.  But updating for\n> rewritten commits fundamentally need to happen in all of these multiple\n> notes trees they would want to read from.\n\nThis patch is the display-several-trees bit.  I'm assuming that it's\nok to load all notes trees at once.  (Obviously it lacks docs, and it\nalso breaks test because of the new formatting that shows 'Notes from\nnotes/someref'.  Please ignore those for now.)\n\nMy actual intent was to allow more than one notes.displayref config\nsetting, and loading all of them.  But this doesn't seem to work; a\ndebugging print confirms that notes_display_config sees the entry, but\nanother debugging print shows that it only ever loads one of them.\nWhat am I doing wrong?\n\nI plan on having another set of configs called notes.rewriteRef that\ngives the list of refs that should have their notes rewritten\nautomatically along with their commits.  [The rewriteMode setting\ncould become '<mode> for <ref>' if we want to let the user configure\nit on a per-ref/glob basis.]\n\nFinally, git-notes should probably grow a --ref option that serves as\na more convenient way of setting GIT_NOTES_REF.\n\n\n notes.c  |   82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++----\n notes.h  |    1 +\n pretty.c |    6 ++--\n 3 files changed, 80 insertions(+), 9 deletions(-)\n\ndiff --git a/notes.c b/notes.c\nindex a5ff723..47eaa21 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,36 @@ int combine_notes_ignore(unsigned char *cur_sha1,\n \treturn 0;\n }\n \n+static int read_one_display_ref(const char *path, const unsigned char *sha1,\n+\t\t\t\tint flag, void *cb)\n+{\n+\tstring_list_insert(path, &display_notes_refs);\n+\treturn 0;\n+}\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\tfor_each_glob_ref(read_one_display_ref, v, NULL);\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 +875,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 +899,29 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \tload_subtree(t, &root_tree, t->root, 0);\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+\tint *counter = cb_data;\n+\tstruct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));\n+\tinit_notes(t, item->string, combine_notes_ignore, 0);\n+\tdisplay_notes_trees[(*counter)++] = t;\n+\treturn 0;\n+}\n+\n+void init_display_notes()\n+{\n+\tint cb_counter = 0;\n+\tstring_list_insert(default_notes_ref(), &display_notes_refs);\n+\tdisplay_notes_refs.strdup_strings = 1;\n+\tgit_config(notes_display_config, NULL);\n+\tdisplay_notes_trees = xmalloc((display_notes_refs.nr+1)\n+\t\t\t\t      * sizeof(struct notes_tree *));\n+\tfor_each_string_list(load_one_display_note_ref,\n+\t\t\t     &display_notes_refs, &cb_counter);\n+\tdisplay_notes_trees[cb_counter] = NULL;\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 +1070,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@@ -1031,6 +1090,17 @@ void format_note(struct notes_tree *t, const unsigned char *object_sha1,\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+}\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)\ndiff --git a/notes.h b/notes.h\nindex b7547bf..f6a5229 100644\n--- a/notes.h\n+++ b/notes.h\n@@ -193,6 +193,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.\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 }\n-- \n1.7.0.143.gbf2a7.dirty\n"},{"id":"135160","messageId":"201002201623.59179.trast@student.ethz.ch","threadId":"22652","inReplyTo":"8c3f1d66d306747d777f04621f1d43d7e07ddc6a.1266677264.git.trast@student.ethz.ch","subject":"Re: [WIP/RFC PATCH] Support showing notes from more than one notes tree","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-02-20T15:23:58Z","receivedAt":"2010-02-20T15:23:58Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"On Saturday 20 February 2010 15:58:47 Thomas Rast wrote:\n> My actual intent was to allow more than one notes.displayref config\n> setting, and loading all of them.  But this doesn't seem to work; a\n> debugging print confirms that notes_display_config sees the entry, but\n> another debugging print shows that it only ever loads one of them.\n> What am I doing wrong?\n\nAh, figured that out myself: for_each_glob_ref has the logic of --glob\nto attach /* if there are no globbing characters.  So I'll add some\ncode to skip for_each_ref and enter it in the list directly if there\nis nothing to glob.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"}]}