{"thread":{"id":"17298","subject":"What's cooking in git.git (Jan 2009, #05; Wed, 21)","startedAt":"2009-01-22T03:55:36Z","lastAt":"2009-02-01T12:01:55Z","messageCount":23,"participants":["Junio C Hamano","Jeff King","Johannes Schindelin","Boyd Stephen Smith Jr.","Johannes Sixt","Sam Vilain","Jakub Narebski"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"101492","messageId":"7vab9kataf.fsf@gitster.siamese.dyndns.org","threadId":"17298","inReplyTo":null,"subject":"What's cooking in git.git (Jan 2009, #05; Wed, 21)","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-01-22T03:55:36Z","receivedAt":"2009-01-22T03:55:36Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Here are the topics that have been cooking.  Commits prefixed with '-' are\nonly in 'pu' while commits prefixed with '+' are in 'next'.  The ones\nmarked with '.' do not appear in any of the branches, but I am still\nholding onto them.\n\nThe topics list the commits in reverse chronological order.  The topics\nmeant to be merged to the maintenance series have \"maint-\" in their names.\n\n----------------------------------------------------------------\n[New Topics]\n\n* js/valgrind (Wed Jan 21 02:36:40 2009 +0100) 2 commits\n - valgrind: ignore ldso errors\n - Add valgrind support in test scripts\n\nDscho seems to have some updates out of discussion with Peff.\n\n----------------------------------------------------------------\n[Stalled and may need help and prodding to go forward]\n\n* jc/blame (Wed Jun 4 22:58:40 2008 -0700) 2 commits\n + blame: show \"previous\" information in --porcelain/--incremental\n   format\n + git-blame: refactor code to emit \"porcelain format\" output\n\nThis gives Porcelains (like gitweb) the information on the commit _before_\nthe one that the final blame is laid on, which should save them one\nrev-parse to dig further.  The line number in the \"previous\" information\nmay need refining, and sanity checking code for reference counting may\nneed to be resurrected before this can move forward.\n\n* db/foreign-scm (Sun Jan 11 15:12:10 2009 -0500) 3 commits\n - Support fetching from foreign VCSes\n - Add specification of git-vcs helpers\n - Add \"vcs\" config option in remotes\n\nThe \"spec\" did not seem quite well cooked yet, but in the longer term I\nthink something like this to allow interoperating with other SCMs as if\nthe other end is a native git repository is a very worthy goal.\n\n----------------------------------------------------------------\n[Actively cooking]\n\n* sp/runtime-prefix (Sun Jan 18 13:00:15 2009 +0100) 7 commits\n - Windows: Revert to default paths and convert them by\n   RUNTIME_PREFIX\n - Compute prefix at runtime if RUNTIME_PREFIX is set\n - Modify setup_path() to only add git_exec_path() to PATH\n - Add calls to git_extract_argv0_path() in programs that call\n   git_config_*\n - git_extract_argv0_path(): Move check for valid argv0 from caller\n   to callee\n - Refactor git_set_argv0_path() to git_extract_argv0_path()\n - Move computation of absolute paths from Makefile to runtime (in\n   preparation for RUNTIME_PREFIX)\n\nWe should move this to 'next' soon with J6t's blessing.\n\n* lh/submodule-tree-traversal (Mon Jan 12 00:45:55 2009 +0100) 3 commits\n + builtin-ls-tree: enable traversal of submodules\n + archive.c: enable traversal of submodules\n + tree.c: add support for traversal of submodules\n\nI think choosing the submodules to descend into by seeing if the commit\nhappens to be available is a horribly broken semantics; it needs to be\nfixed before this can move to 'master'.\n\n* jk/signal-cleanup (Sun Jan 11 06:36:49 2009 -0500) 3 commits\n - pager: do wait_for_pager on signal death\n - refactor signal handling for cleanup functions\n - chain kill signals for cleanup functions\n\nI think this can move to 'next', as Peff and J6t agreed on how to fix\nthings up as needed for Windows.\n\n* ks/maint-mailinfo-folded (Tue Jan 13 01:21:04 2009 +0300) 5 commits\n - mailinfo: tests for RFC2047 examples\n - mailinfo: add explicit test for mails like '<a.u.thor@example.com>\n   (A U Thor)'\n - mailinfo: more smarter removal of rfc822 comments from 'From'\n + mailinfo: 'From:' header should be unfold as well\n + mailinfo: correctly handle multiline 'Subject:' header\n\nAs far as I can see, the only remaining thing is a minor fix-up in the\n\"comment removal\" one before we can move this fully to 'next'.\n\n* js/notes (Tue Jan 13 20:57:16 2009 +0100) 6 commits\n + git-notes: fix printing of multi-line notes\n + notes: fix core.notesRef documentation\n + Add an expensive test for git-notes\n + Speed up git notes lookup\n + Add a script to edit/inspect notes\n + Introduce commit notes\n\nIt would be nice to hear a real world success story using the notes\nmechanism before casting this design in stone.\n\n* sc/gitweb-category (Fri Dec 12 00:45:12 2008 +0100) 3 commits\n - gitweb: Optional grouping of projects by category\n - gitweb: Split git_project_list_body in two functions\n - gitweb: Modularized git_get_project_description to be more generic\n\nDesign discussion between Jakub and Sebastien continues.\n\n----------------------------------------------------------------\n[Graduated to \"master\"]\n\n* jk/color-parse (Sat Jan 17 10:38:46 2009 -0500) 2 commits\n + expand --pretty=format color options\n + color: make it easier for non-config to parse color specs\n\n* sb/hook-cleanup (Sat Jan 17 04:02:55 2009 +0100) 5 commits\n + run_hook(): allow more than 9 hook arguments\n + run_hook(): check the executability of the hook before filling\n   argv\n + api-run-command.txt: talk about run_hook()\n + Move run_hook() from builtin-commit.c into run-command.c (libgit)\n + checkout: don't crash on file checkout before running post-\n   checkout hook\n\n* rs/ctype (Sat Jan 17 16:50:37 2009 +0100) 4 commits\n + Add is_regex_special()\n + Change NUL char handling of isspecial()\n + Reformat ctype.c\n + Add ctype test\n\n* jf/am-failure-report (Sun Jan 18 19:34:31 2009 -0800) 2 commits\n + git-am: re-fix the diag message printing\n + git-am: Make it easier to see which patch failed\n\n* sg/maint-gitdir-in-subdir (Fri Jan 16 16:37:33 2009 +0100) 1 commit\n + Fix gitdir detection when in subdir of gitdir\n\nThis has my \"don't do the fullpath if you are directly inside .git\"\nsquashed in, so it should be much safer.\n\n* am/maint-push-doc (Sun Jan 18 15:36:58 2009 +0100) 4 commits\n + Documentation: avoid using undefined parameters\n + Documentation: mention branches rather than heads\n + Documentation: remove a redundant elaboration\n + Documentation: git push repository can also be a remote\n\n* lt/maint-wrap-zlib (Wed Jan 7 19:54:47 2009 -0800) 1 commit\n + Wrap inflate and other zlib routines for better error reporting\n\nNeeds the \"free our memory upon seeing Z_MEM_ERROR and try again\" bits\nextracted from Shawn's patch on top of this one.\n\n* kb/am-directory (Wed Jan 14 16:29:59 2009 -0800) 2 commits\n + git-am: fix shell quoting\n + git-am: add --directory=<dir> option\n\nThis is \"third-time-lucky, perhaps?\" resurrection.  I do not think I'd be\nusing this very often, but it originated from a real user request.\n\n* jc/maint-format-patch-o-relative (Mon Jan 12 15:18:02 2009 -0800) 1 commit\n + Teach format-patch to handle output directory relative to cwd\n\n----------------------------------------------------------------\n[Will merge to \"master\" soon]\n\n* kb/lstat-cache (Sun Jan 18 16:14:54 2009 +0100) 5 commits\n + lstat_cache(): introduce clear_lstat_cache() function\n + lstat_cache(): introduce invalidate_lstat_cache() function\n + lstat_cache(): introduce has_dirs_only_path() function\n + lstat_cache(): introduce has_symlink_or_noent_leading_path()\n   function\n + lstat_cache(): more cache effective symlink/directory detection\n\n* tr/previous-branch (Wed Jan 21 00:37:38 2009 -0800) 10 commits\n + Simplify parsing branch switching events in reflog\n + Introduce for_each_recent_reflog_ent().\n + interpret_nth_last_branch(): plug small memleak\n + Fix reflog parsing for a malformed branch switching entry\n + Fix parsing of @{-1}@{1}\n + interpret_nth_last_branch(): avoid traversing the reflog twice\n + checkout: implement \"-\" abbreviation, add docs and tests\n + sha1_name: support @{-N} syntax in get_sha1()\n + sha1_name: tweak @{-N} lookup\n + checkout: implement \"@{-N}\" shortcut name for N-th last branch\n\n* js/maint-all-implies-HEAD (Sat Jan 17 22:27:08 2009 -0800) 2 commits\n + bundle: allow the same ref to be given more than once\n + revision walker: include a detached HEAD in --all\n\n* mh/unify-color (Sun Jan 18 21:39:12 2009 +0100) 2 commits\n + move the color variables to color.c\n + handle color.ui at a central place\n\n* cb/add-pathspec (Wed Jan 14 15:54:35 2009 +0100) 2 commits\n + remove pathspec_match, use match_pathspec instead\n + clean up pathspec matching\n\n* js/diff-color-words (Tue Jan 20 21:46:57 2009 -0600) 8 commits\n + color-words: Support diff.wordregex config option\n + color-words: make regex configurable via attributes\n + color-words: expand docs with precise semantics\n + color-words: enable REG_NEWLINE to help user\n + color-words: take an optional regular expression describing words\n + color-words: change algorithm to allow for 0-character word\n   boundaries\n + color-words: refactor word splitting and use ALLOC_GROW()\n + Add color_fwrite_lines(), a function coloring each line\n   individually\n\n* js/patience-diff (Thu Jan 1 17:39:37 2009 +0100) 3 commits\n + bash completions: Add the --patience option\n + Introduce the diff option '--patience'\n + Implement the patience diff algorithm\n\n----------------------------------------------------------------\n[On Hold]\n\n* jk/renamelimit (Sat May 3 13:58:42 2008 -0700) 1 commit\n . diff: enable \"too large a rename\" warning when -M/-C is explicitly\n   asked for\n\n* jc/stripspace (Sun Mar 9 00:30:35 2008 -0800) 6 commits\n . git-am --forge: add Signed-off-by: line for the author\n . git-am: clean-up Signed-off-by: lines\n . stripspace: add --log-clean option to clean up signed-off-by:\n   lines\n . stripspace: use parse_options()\n . Add \"git am -s\" test\n . git-am: refactor code to add signed-off-by line for the committer\n\n* jc/post-simplify (Fri Aug 15 01:34:51 2008 -0700) 2 commits\n . revision --simplify-merges: incremental simplification\n . revision --simplify-merges: prepare for incremental simplification\n\n* wp/add-patch-find (Thu Nov 27 04:08:03 2008 +0000) 3 commits\n . In add --patch, Handle K,k,J,j slightly more gracefully.\n . Add / command in add --patch\n . git-add -i/-p: Change prompt separater from slash to comma\n\n* jc/grafts (Wed Jul 2 17:14:12 2008 -0700) 1 commit\n . [BROKEN wrt shallow clones] Ignore graft during object transfer\n\n* jc/replace (Fri Oct 31 09:21:39 2008 -0700) 1 commit\n . WIP\n"},{"id":"101494","messageId":"20090122042643.GB31427@coredump.intra.peff.net","threadId":"17298","inReplyTo":"7vab9kataf.fsf@gitster.siamese.dyndns.org","subject":"Re: What's cooking in git.git (Jan 2009, #05; Wed, 21)","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-01-22T04:26:43Z","receivedAt":"2009-01-22T04:26:43Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jan 21, 2009 at 07:55:36PM -0800, Junio C Hamano wrote:\n\n> * jk/signal-cleanup (Sun Jan 11 06:36:49 2009 -0500) 3 commits\n>  - pager: do wait_for_pager on signal death\n>  - refactor signal handling for cleanup functions\n>  - chain kill signals for cleanup functions\n> \n> I think this can move to 'next', as Peff and J6t agreed on how to fix\n> things up as needed for Windows.\n\nPlease wait for my re-roll, which I'll send in a few minutes.\n\n-Peff\n"},{"id":"101498","messageId":"alpine.DEB.1.00.0901220606040.3586@pacific.mpi-cbg.de","threadId":"17298","inReplyTo":"7vab9kataf.fsf@gitster.siamese.dyndns.org","subject":"Re: What's cooking in git.git (Jan 2009, #05; Wed, 21)","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-22T05:13:01Z","receivedAt":"2009-01-22T05:13:01Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 21 Jan 2009, Junio C Hamano wrote:\n\n> * js/notes (Tue Jan 13 20:57:16 2009 +0100) 6 commits\n>  + git-notes: fix printing of multi-line notes\n>  + notes: fix core.notesRef documentation\n>  + Add an expensive test for git-notes\n>  + Speed up git notes lookup\n>  + Add a script to edit/inspect notes\n>  + Introduce commit notes\n> \n> It would be nice to hear a real world success story using the notes\n> mechanism before casting this design in stone.\n\nI'd like to have some profiling done before that.  For example, I am still \na bit unsure how the things would perform with a 50-deep delta chain for \na notes tree having 50,000+ notes in it (which I think will not be all \nthat unreasonable for a medium-sized project that stores bug-tracking \ninformation in the notes).\n\nI have a gut feeling that the performance dip I saw is a direct result of \ndoing away with the fan-out \"subdirectories\": remember, originally, I had \na tree structure much like the loose objects in .git/objects/??/, while \nPeff convinced me that a flat tree object should be enough.\n\nI could be wrong on that, though.\n\n> * js/patience-diff (Thu Jan 1 17:39:37 2009 +0100) 3 commits\n>  + bash completions: Add the --patience option\n>  + Introduce the diff option '--patience'\n>  + Implement the patience diff algorithm\n\nThere is this one issue that my patience's output differs from bzr's.  \nSince the patience diff algorithm is so lousily documented, I do not know \nif it is due to my misunderstanding the algorithm, or due to bzr doing \nsomething clever in addition.\n\nI'd be thankful if somebody could clarify that issue.\n\nCiao,\nDscho\n"},{"id":"101499","messageId":"200901212321.50526.bss@iguanasuicide.net","threadId":"17298","inReplyTo":"7vab9kataf.fsf@gitster.siamese.dyndns.org","subject":"Re: What's cooking in git.git (Jan 2009, #05; Wed, 21)","fromName":"Boyd Stephen Smith Jr.","fromEmail":"bss@iguanasuicide.net","sentAt":"2009-01-22T05:21:34Z","receivedAt":"2009-01-22T05:21:34Z","isPatch":false,"sender":{"key":"bss@iguanasuicide.net","avatar":"https://gravatar.com/avatar/84b95eeff194b816c1568b1339e63e4b229825298664a9037b9f1ec713ead1e3?d=mp&s=160"},"body":"On Wednesday 21 January 2009, Junio C Hamano <gitster@pobox.com> wrote \nabout 'What's cooking in git.git (Jan 2009, #05; Wed, 21)':\n>* js/notes (Tue Jan 13 20:57:16 2009 +0100) 6 commits\n> + git-notes: fix printing of multi-line notes\n> + notes: fix core.notesRef documentation\n> + Add an expensive test for git-notes\n> + Speed up git notes lookup\n> + Add a script to edit/inspect notes\n> + Introduce commit notes\n>\n>It would be nice to hear a real world success story using the notes\n>mechanism before casting this design in stone.\n\nI'll see if I can't try to put this through some paces over the week.  \nAlso, I'd like to see some support for notes in push/fetch, but it could \ncertainly be added afterwards.\n\n>* js/diff-color-words (Tue Jan 20 21:46:57 2009 -0600) 8 commits\n> + color-words: Support diff.wordregex config option\n> + color-words: make regex configurable via attributes\n> + color-words: expand docs with precise semantics\n> + color-words: enable REG_NEWLINE to help user\n> + color-words: take an optional regular expression describing words\n> + color-words: change algorithm to allow for 0-character word\n>   boundaries\n> + color-words: refactor word splitting and use ALLOC_GROW()\n> + Add color_fwrite_lines(), a function coloring each line\n>   individually\n\nI think my patch in \nhttp://thread.gmane.org/gmane.comp.version-control.git/106567 should be \napplied to the top of this.  It respells \"wordregex\" to match existing \nuses throughout the repo.  Dscho had issues with one hunk, but I think I \naddressed them in my follow-up.  It looks like 98a4d87b (color-words: \nSupport diff.wordregex config option) still has the internally-consistent \nruntogether spelling that doesn't match other configuration variables etc.\n-- \nBoyd Stephen Smith Jr.                     ,= ,-_-. =. \nbss@iguanasuicide.net                     ((_/)o o(\\_))\nICQ: 514984 YM/AIM: DaTwinkDaddy           `-'(. .)`-' \nhttp://iguanasuicide.net/                      \\_/     \n"},{"id":"101501","messageId":"20090122055734.GA30133@coredump.intra.peff.net","threadId":"17298","inReplyTo":"20090122042643.GB31427@coredump.intra.peff.net","subject":"[PATCH v2 1/5] Windows: Fix signal numbers","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-01-22T05:57:34Z","receivedAt":"2009-01-22T05:57:34Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Johannes Sixt <j6t@kdbg.org>\n\nWe had defined some SIG_FOO macros that appear in the code, but that are\nnot supported on Windows, in order to make the code compile.  But a\nsubsequent change will assert that a signal number is non-zero.  We now\nuse the signal numbers that are commonly used on POSIX systems.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\nSigned-off-by: Jeff King <peff@peff.net>\n---\nThis is necessary to avoid violating sigchain assertions in the next\npatch.\n\n compat/mingw.h |   12 ++++++------\n 1 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/compat/mingw.h b/compat/mingw.h\nindex 4f275cb..a255898 100644\n--- a/compat/mingw.h\n+++ b/compat/mingw.h\n@@ -21,12 +21,12 @@ typedef int pid_t;\n #define WEXITSTATUS(x) ((x) & 0xff)\n #define WIFSIGNALED(x) ((unsigned)(x) > 259)\n \n-#define SIGKILL 0\n-#define SIGCHLD 0\n-#define SIGPIPE 0\n-#define SIGHUP 0\n-#define SIGQUIT 0\n-#define SIGALRM 100\n+#define SIGHUP 1\n+#define SIGQUIT 3\n+#define SIGKILL 9\n+#define SIGPIPE 13\n+#define SIGALRM 14\n+#define SIGCHLD 17\n \n #define F_GETFD 1\n #define F_SETFD 2\n-- \n1.6.1.403.g6c435\n"},{"id":"101502","messageId":"20090122055956.GB30133@coredump.intra.peff.net","threadId":"17298","inReplyTo":"20090122042643.GB31427@coredump.intra.peff.net","subject":"[PATCH v2 2/5] diff: refactor tempfile cleanup handling","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-01-22T05:59:56Z","receivedAt":"2009-01-22T05:59:56Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"There are two pieces of code that create tempfiles for diff:\nrun_external_diff and run_textconv. The former cleans up its\ntempfiles in the face of premature death (i.e., by die() or\nby signal), but the latter does not. After this patch, they\nwill both use the same cleanup routines.\n\nTo make clear what the change is, let me first explain what\nhappens now:\n\n  - run_external_diff uses a static global array of 2\n    diff_tempfile structs (since it knows it will always\n    need exactly 2 tempfiles). It calls prepare_temp_file\n    (which doesn't know anything about the global array) on\n    each of the structs, creating the tempfiles that need to\n    be cleaned up. It then registers atexit and signal\n    handlers to look through the global array and remove the\n    tempfiles. If it succeeds, it calls the handler manually\n    (which marks the tempfile structs as unused).\n\n  - textconv has its own tempfile struct, which it allocates\n    using prepare_temp_file and cleans up manually. No\n    signal or atexit handlers.\n\nThe new code moves the installation of cleanup handlers into\nthe prepare_temp_file function. Which means that that\nfunction now has to understand that there is static tempfile\nstorage. So what happens now is:\n\n  - run_external_diff calls prepare_temp_file\n  - prepare_temp_file calls claim_diff_tempfile, which\n    allocates an unused slot from our global array\n  - prepare_temp_file installs (if they have not already\n    been installed) atexit and signal handlers for cleanup\n  - prepare_temp_file sets up the tempfile as usual\n  - prepare_temp_file returns a pointer to the allocated\n    tempfile\n\nThe advantage being that run_external_diff no longer has to\ncare about setting up cleanup handlers. Now by virtue of\ncalling prepare_temp_file, run_textconv gets the same\nbenefit, as will any future users of prepare_temp_file.\n\nThere are also a few side benefits to the specific\nimplementation:\n\n  - we now install cleanup handlers _before_ allocating the\n    tempfile, closing a race which could leave temp cruft\n\n  - when allocating a slot in the global array, we will now\n    detect a situation where the old slots were not properly\n    vacated (i.e., somebody forgot to call remove upon\n    leaving the function). In the old code, such a situation\n    would silently overwrite the tempfile names, meaning we\n    would forget to clean them up. The new code dies with a\n    bug warning.\n\n  - we make sure only to install the signal handler once.\n    This isn't a big deal, since we are just overwriting the\n    old handler, but will become an issue when a later patch\n    converts the code to use sigchain\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nThis patch is new since v1. I started with just looking at whether it\nwas safe to move the signal() call, but there were really several things\nto be cleaned up, and it just made sense to do them all together.\n\n diff.c |  107 +++++++++++++++++++++++++++++++++-------------------------------\n 1 files changed, 55 insertions(+), 52 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 0731313..ae6d552 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -167,6 +167,33 @@ static struct diff_tempfile {\n \tchar tmp_path[PATH_MAX];\n } diff_temp[2];\n \n+static struct diff_tempfile *claim_diff_tempfile(void) {\n+\tint i;\n+\tfor (i = 0; i < ARRAY_SIZE(diff_temp); i++)\n+\t\tif (!diff_temp[i].name)\n+\t\t\treturn diff_temp + i;\n+\tdie(\"BUG: diff is failing to clean up its tempfiles\");\n+}\n+\n+static int remove_tempfile_installed;\n+\n+static void remove_tempfile(void)\n+{\n+\tint i;\n+\tfor (i = 0; i < ARRAY_SIZE(diff_temp); i++)\n+\t\tif (diff_temp[i].name == diff_temp[i].tmp_path) {\n+\t\t\tunlink(diff_temp[i].name);\n+\t\t\tdiff_temp[i].name = NULL;\n+\t\t}\n+}\n+\n+static void remove_tempfile_on_signal(int signo)\n+{\n+\tremove_tempfile();\n+\tsignal(SIGINT, SIG_DFL);\n+\traise(signo);\n+}\n+\n static int count_lines(const char *data, int size)\n {\n \tint count, ch, completely_empty = 1, nl_just_seen = 0;\n@@ -1859,10 +1886,11 @@ static void prep_temp_blob(struct diff_tempfile *temp,\n \tsprintf(temp->mode, \"%06o\", mode);\n }\n \n-static void prepare_temp_file(const char *name,\n-\t\t\t      struct diff_tempfile *temp,\n-\t\t\t      struct diff_filespec *one)\n+static struct diff_tempfile *prepare_temp_file(const char *name,\n+\t\tstruct diff_filespec *one)\n {\n+\tstruct diff_tempfile *temp = claim_diff_tempfile();\n+\n \tif (!DIFF_FILE_VALID(one)) {\n \tnot_a_valid_file:\n \t\t/* A '-' entry produces this for file-2, and\n@@ -1871,7 +1899,13 @@ static void prepare_temp_file(const char *name,\n \t\ttemp->name = \"/dev/null\";\n \t\tstrcpy(temp->hex, \".\");\n \t\tstrcpy(temp->mode, \".\");\n-\t\treturn;\n+\t\treturn temp;\n+\t}\n+\n+\tif (!remove_tempfile_installed) {\n+\t\tatexit(remove_tempfile);\n+\t\tsignal(SIGINT, remove_tempfile_on_signal);\n+\t\tremove_tempfile_installed = 1;\n \t}\n \n \tif (!one->sha1_valid ||\n@@ -1911,7 +1945,7 @@ static void prepare_temp_file(const char *name,\n \t\t\t */\n \t\t\tsprintf(temp->mode, \"%06o\", one->mode);\n \t\t}\n-\t\treturn;\n+\t\treturn temp;\n \t}\n \telse {\n \t\tif (diff_populate_filespec(one, 0))\n@@ -1919,24 +1953,7 @@ static void prepare_temp_file(const char *name,\n \t\tprep_temp_blob(temp, one->data, one->size,\n \t\t\t       one->sha1, one->mode);\n \t}\n-}\n-\n-static void remove_tempfile(void)\n-{\n-\tint i;\n-\n-\tfor (i = 0; i < 2; i++)\n-\t\tif (diff_temp[i].name == diff_temp[i].tmp_path) {\n-\t\t\tunlink(diff_temp[i].name);\n-\t\t\tdiff_temp[i].name = NULL;\n-\t\t}\n-}\n-\n-static void remove_tempfile_on_signal(int signo)\n-{\n-\tremove_tempfile();\n-\tsignal(SIGINT, SIG_DFL);\n-\traise(signo);\n+\treturn temp;\n }\n \n /* An external diff command takes:\n@@ -1954,34 +1971,22 @@ static void run_external_diff(const char *pgm,\n \t\t\t      int complete_rewrite)\n {\n \tconst char *spawn_arg[10];\n-\tstruct diff_tempfile *temp = diff_temp;\n \tint retval;\n-\tstatic int atexit_asked = 0;\n-\tconst char *othername;\n \tconst char **arg = &spawn_arg[0];\n \n-\tothername = (other? other : name);\n-\tif (one && two) {\n-\t\tprepare_temp_file(name, &temp[0], one);\n-\t\tprepare_temp_file(othername, &temp[1], two);\n-\t\tif (! atexit_asked &&\n-\t\t    (temp[0].name == temp[0].tmp_path ||\n-\t\t     temp[1].name == temp[1].tmp_path)) {\n-\t\t\tatexit_asked = 1;\n-\t\t\tatexit(remove_tempfile);\n-\t\t}\n-\t\tsignal(SIGINT, remove_tempfile_on_signal);\n-\t}\n-\n \tif (one && two) {\n+\t\tstruct diff_tempfile *temp_one, *temp_two;\n+\t\tconst char *othername = (other ? other : name);\n+\t\ttemp_one = prepare_temp_file(name, one);\n+\t\ttemp_two = prepare_temp_file(othername, two);\n \t\t*arg++ = pgm;\n \t\t*arg++ = name;\n-\t\t*arg++ = temp[0].name;\n-\t\t*arg++ = temp[0].hex;\n-\t\t*arg++ = temp[0].mode;\n-\t\t*arg++ = temp[1].name;\n-\t\t*arg++ = temp[1].hex;\n-\t\t*arg++ = temp[1].mode;\n+\t\t*arg++ = temp_one->name;\n+\t\t*arg++ = temp_one->hex;\n+\t\t*arg++ = temp_one->mode;\n+\t\t*arg++ = temp_two->name;\n+\t\t*arg++ = temp_two->hex;\n+\t\t*arg++ = temp_two->mode;\n \t\tif (other) {\n \t\t\t*arg++ = other;\n \t\t\t*arg++ = xfrm_msg;\n@@ -3450,15 +3455,15 @@ void diff_unmerge(struct diff_options *options,\n static char *run_textconv(const char *pgm, struct diff_filespec *spec,\n \t\tsize_t *outsize)\n {\n-\tstruct diff_tempfile temp;\n+\tstruct diff_tempfile *temp;\n \tconst char *argv[3];\n \tconst char **arg = argv;\n \tstruct child_process child;\n \tstruct strbuf buf = STRBUF_INIT;\n \n-\tprepare_temp_file(spec->path, &temp, spec);\n+\ttemp = prepare_temp_file(spec->path, spec);\n \t*arg++ = pgm;\n-\t*arg++ = temp.name;\n+\t*arg++ = temp->name;\n \t*arg = NULL;\n \n \tmemset(&child, 0, sizeof(child));\n@@ -3467,13 +3472,11 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec,\n \tif (start_command(&child) != 0 ||\n \t    strbuf_read(&buf, child.out, 0) < 0 ||\n \t    finish_command(&child) != 0) {\n-\t\tif (temp.name == temp.tmp_path)\n-\t\t\tunlink(temp.name);\n+\t\tremove_tempfile();\n \t\terror(\"error running textconv command '%s'\", pgm);\n \t\treturn NULL;\n \t}\n-\tif (temp.name == temp.tmp_path)\n-\t\tunlink(temp.name);\n+\tremove_tempfile();\n \n \treturn strbuf_detach(&buf, outsize);\n }\n-- \n1.6.1.403.g6c435\n"},{"id":"101503","messageId":"20090122060235.GC30133@coredump.intra.peff.net","threadId":"17298","inReplyTo":"20090122042643.GB31427@coredump.intra.peff.net","subject":"[PATCH v2 3/5] chain kill signals for cleanup functions","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-01-22T06:02:35Z","receivedAt":"2009-01-22T06:02:35Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"If a piece of code wanted to do some cleanup before exiting\n(e.g., cleaning up a lockfile or a tempfile), our usual\nstrategy was to install a signal handler that did something\nlike this:\n\n  do_cleanup(); /* actual work */\n  signal(signo, SIG_DFL); /* restore previous behavior */\n  raise(signo); /* deliver signal, killing ourselves */\n\nFor a single handler, this works fine. However, if we want\nto clean up two _different_ things, we run into a problem.\nThe most recently installed handler will run, but when it\nremoves itself as a handler, it doesn't put back the first\nhandler.\n\nThis patch introduces sigchain, a tiny library for handling\na stack of signal handlers. You sigchain_push each handler,\nand use sigchain_pop to restore whoever was before you in\nthe stack.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nTwo changes since last time:\n\n - rebased on new 2/5, which fixes a problem the original had in calling\n   sigchain_push every time you did an external diff\n\n - I tried to handle all systems in the test script, which should\n   hopefully now pass on Windows.\n\n .gitignore            |    1 +\n Makefile              |    3 +++\n builtin-clone.c       |    5 +++--\n builtin-fetch--tool.c |    5 +++--\n builtin-fetch.c       |    5 +++--\n diff.c                |    5 +++--\n http-push.c           |   11 ++++++-----\n lockfile.c            |   13 +++++++------\n sigchain.c            |   43 +++++++++++++++++++++++++++++++++++++++++++\n sigchain.h            |    9 +++++++++\n t/t0005-signals.sh    |   22 ++++++++++++++++++++++\n test-sigchain.c       |   22 ++++++++++++++++++++++\n 12 files changed, 125 insertions(+), 19 deletions(-)\n create mode 100644 sigchain.c\n create mode 100644 sigchain.h\n create mode 100755 t/t0005-signals.sh\n create mode 100644 test-sigchain.c\n\ndiff --git a/.gitignore b/.gitignore\nindex d9adce5..f28a54d 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -152,6 +152,7 @@ test-match-trees\n test-parse-options\n test-path-utils\n test-sha1\n+test-sigchain\n common-cmds.h\n *.tar.gz\n *.dsc\ndiff --git a/Makefile b/Makefile\nindex 270b223..30371d1 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -388,6 +388,7 @@ LIB_H += revision.h\n LIB_H += run-command.h\n LIB_H += sha1-lookup.h\n LIB_H += sideband.h\n+LIB_H += sigchain.h\n LIB_H += strbuf.h\n LIB_H += tag.h\n LIB_H += transport.h\n@@ -481,6 +482,7 @@ LIB_OBJS += sha1-lookup.o\n LIB_OBJS += sha1_name.o\n LIB_OBJS += shallow.o\n LIB_OBJS += sideband.o\n+LIB_OBJS += sigchain.o\n LIB_OBJS += strbuf.o\n LIB_OBJS += symlinks.o\n LIB_OBJS += tag.o\n@@ -1365,6 +1367,7 @@ TEST_PROGRAMS += test-match-trees$X\n TEST_PROGRAMS += test-parse-options$X\n TEST_PROGRAMS += test-path-utils$X\n TEST_PROGRAMS += test-sha1$X\n+TEST_PROGRAMS += test-sigchain$X\n \n all:: $(TEST_PROGRAMS)\n \ndiff --git a/builtin-clone.c b/builtin-clone.c\nindex f7e5a7b..849cefc 100644\n--- a/builtin-clone.c\n+++ b/builtin-clone.c\n@@ -19,6 +19,7 @@\n #include \"strbuf.h\"\n #include \"dir.h\"\n #include \"pack-refs.h\"\n+#include \"sigchain.h\"\n \n /*\n  * Overall FIXMEs:\n@@ -288,7 +289,7 @@ static void remove_junk(void)\n static void remove_junk_on_signal(int signo)\n {\n \tremove_junk();\n-\tsignal(SIGINT, SIG_DFL);\n+\tsigchain_pop(signo);\n \traise(signo);\n }\n \n@@ -441,7 +442,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \tjunk_git_dir = git_dir;\n \tatexit(remove_junk);\n-\tsignal(SIGINT, remove_junk_on_signal);\n+\tsigchain_push(SIGINT, remove_junk_on_signal);\n \n \tsetenv(CONFIG_ENVIRONMENT, xstrdup(mkpath(\"%s/config\", git_dir)), 1);\n \ndiff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c\nindex 469b07e..b1d7f8f 100644\n--- a/builtin-fetch--tool.c\n+++ b/builtin-fetch--tool.c\n@@ -2,6 +2,7 @@\n #include \"cache.h\"\n #include \"refs.h\"\n #include \"commit.h\"\n+#include \"sigchain.h\"\n \n static char *get_stdin(void)\n {\n@@ -186,7 +187,7 @@ static void remove_keep(void)\n static void remove_keep_on_signal(int signo)\n {\n \tremove_keep();\n-\tsignal(SIGINT, SIG_DFL);\n+\tsigchain_pop(signo);\n \traise(signo);\n }\n \n@@ -245,7 +246,7 @@ static int fetch_native_store(FILE *fp,\n \tchar buffer[1024];\n \tint err = 0;\n \n-\tsignal(SIGINT, remove_keep_on_signal);\n+\tsigchain_push(SIGINT, remove_keep_on_signal);\n \tatexit(remove_keep);\n \n \twhile (fgets(buffer, sizeof(buffer), stdin)) {\ndiff --git a/builtin-fetch.c b/builtin-fetch.c\nindex de6f307..8c86974 100644\n--- a/builtin-fetch.c\n+++ b/builtin-fetch.c\n@@ -10,6 +10,7 @@\n #include \"transport.h\"\n #include \"run-command.h\"\n #include \"parse-options.h\"\n+#include \"sigchain.h\"\n \n static const char * const builtin_fetch_usage[] = {\n \t\"git fetch [options] [<repository> <refspec>...]\",\n@@ -58,7 +59,7 @@ static void unlock_pack(void)\n static void unlock_pack_on_signal(int signo)\n {\n \tunlock_pack();\n-\tsignal(SIGINT, SIG_DFL);\n+\tsigchain_pop(signo);\n \traise(signo);\n }\n \n@@ -672,7 +673,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)\n \t\tref_nr = j;\n \t}\n \n-\tsignal(SIGINT, unlock_pack_on_signal);\n+\tsigchain_push(SIGINT, unlock_pack_on_signal);\n \tatexit(unlock_pack);\n \texit_code = do_fetch(transport,\n \t\t\tparse_fetch_refspec(ref_nr, refs), ref_nr);\ndiff --git a/diff.c b/diff.c\nindex ae6d552..dacd5d2 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -12,6 +12,7 @@\n #include \"run-command.h\"\n #include \"utf8.h\"\n #include \"userdiff.h\"\n+#include \"sigchain.h\"\n \n #ifdef NO_FAST_WORKING_DIRECTORY\n #define FAST_WORKING_DIRECTORY 0\n@@ -190,7 +191,7 @@ static void remove_tempfile(void)\n static void remove_tempfile_on_signal(int signo)\n {\n \tremove_tempfile();\n-\tsignal(SIGINT, SIG_DFL);\n+\tsigchain_pop(signo);\n \traise(signo);\n }\n \n@@ -1904,7 +1905,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name,\n \n \tif (!remove_tempfile_installed) {\n \t\tatexit(remove_tempfile);\n-\t\tsignal(SIGINT, remove_tempfile_on_signal);\n+\t\tsigchain_push(SIGINT, remove_tempfile_on_signal);\n \t\tremove_tempfile_installed = 1;\n \t}\n \ndiff --git a/http-push.c b/http-push.c\nindex cb5bf95..4c92f80 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -10,6 +10,7 @@\n #include \"exec_cmd.h\"\n #include \"remote.h\"\n #include \"list-objects.h\"\n+#include \"sigchain.h\"\n \n #include <expat.h>\n \n@@ -1364,7 +1365,7 @@ static void remove_locks(void)\n static void remove_locks_on_signal(int signo)\n {\n \tremove_locks();\n-\tsignal(signo, SIG_DFL);\n+\tsigchain_pop(signo);\n \traise(signo);\n }\n \n@@ -2266,10 +2267,10 @@ int main(int argc, char **argv)\n \t\tgoto cleanup;\n \t}\n \n-\tsignal(SIGINT, remove_locks_on_signal);\n-\tsignal(SIGHUP, remove_locks_on_signal);\n-\tsignal(SIGQUIT, remove_locks_on_signal);\n-\tsignal(SIGTERM, remove_locks_on_signal);\n+\tsigchain_push(SIGINT, remove_locks_on_signal);\n+\tsigchain_push(SIGHUP, remove_locks_on_signal);\n+\tsigchain_push(SIGQUIT, remove_locks_on_signal);\n+\tsigchain_push(SIGTERM, remove_locks_on_signal);\n \n \t/* Check whether the remote has server info files */\n \tremote->can_update_info_refs = 0;\ndiff --git a/lockfile.c b/lockfile.c\nindex 8589155..3cd57dc 100644\n--- a/lockfile.c\n+++ b/lockfile.c\n@@ -2,6 +2,7 @@\n  * Copyright (c) 2005, Junio C Hamano\n  */\n #include \"cache.h\"\n+#include \"sigchain.h\"\n \n static struct lock_file *lock_file_list;\n static const char *alternate_index_output;\n@@ -24,7 +25,7 @@ static void remove_lock_file(void)\n static void remove_lock_file_on_signal(int signo)\n {\n \tremove_lock_file();\n-\tsignal(signo, SIG_DFL);\n+\tsigchain_pop(signo);\n \traise(signo);\n }\n \n@@ -136,11 +137,11 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)\n \tlk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);\n \tif (0 <= lk->fd) {\n \t\tif (!lock_file_list) {\n-\t\t\tsignal(SIGINT, remove_lock_file_on_signal);\n-\t\t\tsignal(SIGHUP, remove_lock_file_on_signal);\n-\t\t\tsignal(SIGTERM, remove_lock_file_on_signal);\n-\t\t\tsignal(SIGQUIT, remove_lock_file_on_signal);\n-\t\t\tsignal(SIGPIPE, remove_lock_file_on_signal);\n+\t\t\tsigchain_push(SIGINT, remove_lock_file_on_signal);\n+\t\t\tsigchain_push(SIGHUP, remove_lock_file_on_signal);\n+\t\t\tsigchain_push(SIGTERM, remove_lock_file_on_signal);\n+\t\t\tsigchain_push(SIGQUIT, remove_lock_file_on_signal);\n+\t\t\tsigchain_push(SIGPIPE, remove_lock_file_on_signal);\n \t\t\tatexit(remove_lock_file);\n \t\t}\n \t\tlk->owner = getpid();\ndiff --git a/sigchain.c b/sigchain.c\nnew file mode 100644\nindex 0000000..a18d505\n--- /dev/null\n+++ b/sigchain.c\n@@ -0,0 +1,43 @@\n+#include \"sigchain.h\"\n+#include \"cache.h\"\n+\n+#define SIGCHAIN_MAX_SIGNALS 32\n+\n+struct sigchain_signal {\n+\tsigchain_fun *old;\n+\tint n;\n+\tint alloc;\n+};\n+static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS];\n+\n+static void check_signum(int sig)\n+{\n+\tif (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS)\n+\t\tdie(\"BUG: signal out of range: %d\", sig);\n+}\n+\n+int sigchain_push(int sig, sigchain_fun f)\n+{\n+\tstruct sigchain_signal *s = signals + sig;\n+\tcheck_signum(sig);\n+\n+\tALLOC_GROW(s->old, s->n + 1, s->alloc);\n+\ts->old[s->n] = signal(sig, f);\n+\tif (s->old[s->n] == SIG_ERR)\n+\t\treturn -1;\n+\ts->n++;\n+\treturn 0;\n+}\n+\n+int sigchain_pop(int sig)\n+{\n+\tstruct sigchain_signal *s = signals + sig;\n+\tcheck_signum(sig);\n+\tif (s->n < 1)\n+\t\treturn 0;\n+\n+\tif (signal(sig, s->old[s->n - 1]) == SIG_ERR)\n+\t\treturn -1;\n+\ts->n--;\n+\treturn 0;\n+}\ndiff --git a/sigchain.h b/sigchain.h\nnew file mode 100644\nindex 0000000..254ebb0\n--- /dev/null\n+++ b/sigchain.h\n@@ -0,0 +1,9 @@\n+#ifndef SIGCHAIN_H\n+#define SIGCHAIN_H\n+\n+typedef void (*sigchain_fun)(int);\n+\n+int sigchain_push(int sig, sigchain_fun f);\n+int sigchain_pop(int sig);\n+\n+#endif /* SIGCHAIN_H */\ndiff --git a/t/t0005-signals.sh b/t/t0005-signals.sh\nnew file mode 100755\nindex 0000000..9707af7\n--- /dev/null\n+++ b/t/t0005-signals.sh\n@@ -0,0 +1,22 @@\n+#!/bin/sh\n+\n+test_description='signals work as we expect'\n+. ./test-lib.sh\n+\n+cat >expect <<EOF\n+three\n+two\n+one\n+EOF\n+\n+test_expect_success 'sigchain works' '\n+\ttest-sigchain >actual\n+\tcase \"$?\" in\n+\t130) true ;; # POSIX w/ SIGINT=2\n+\t  3) true ;; # Windows\n+\t  *) false ;;\n+\tesac &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\ndiff --git a/test-sigchain.c b/test-sigchain.c\nnew file mode 100644\nindex 0000000..8747dea\n--- /dev/null\n+++ b/test-sigchain.c\n@@ -0,0 +1,22 @@\n+#include \"sigchain.h\"\n+#include \"cache.h\"\n+\n+#define X(f) \\\n+static void f(int sig) { \\\n+\tputs(#f); \\\n+\tfflush(stdout); \\\n+\tsigchain_pop(sig); \\\n+\traise(sig); \\\n+}\n+X(one)\n+X(two)\n+X(three)\n+#undef X\n+\n+int main(int argc, char **argv) {\n+\tsigchain_push(SIGINT, one);\n+\tsigchain_push(SIGINT, two);\n+\tsigchain_push(SIGINT, three);\n+\traise(SIGINT);\n+\treturn 0;\n+}\n-- \n1.6.1.403.g6c435\n"},{"id":"101504","messageId":"20090122060307.GD30133@coredump.intra.peff.net","threadId":"17298","inReplyTo":"20090122042643.GB31427@coredump.intra.peff.net","subject":"[PATCH v2 4/5] refactor signal handling for cleanup functions","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-01-22T06:03:08Z","receivedAt":"2009-01-22T06:03:08Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The current code is very inconsistent about which signals\nare caught for doing cleanup of temporary files and lock\nfiles. Some callsites checked only SIGINT, while others\nchecked a variety of death-dealing signals.\n\nThis patch factors out those signals to a single function,\nand then calls it everywhere. For some sites, that means\nthis is a simple clean up. For others, it is an improvement\nin that they will now properly clean themselves up after a\nlarger variety of signals.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nSame as before, but needed rebasing due to previous diff.c changes.\n\n builtin-clone.c       |    2 +-\n builtin-fetch--tool.c |    2 +-\n builtin-fetch.c       |    2 +-\n diff.c                |    2 +-\n http-push.c           |    5 +----\n lockfile.c            |    6 +-----\n sigchain.c            |    9 +++++++++\n sigchain.h            |    2 ++\n 8 files changed, 17 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin-clone.c b/builtin-clone.c\nindex 849cefc..313df6a 100644\n--- a/builtin-clone.c\n+++ b/builtin-clone.c\n@@ -442,7 +442,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \tjunk_git_dir = git_dir;\n \tatexit(remove_junk);\n-\tsigchain_push(SIGINT, remove_junk_on_signal);\n+\tsigchain_push_common(remove_junk_on_signal);\n \n \tsetenv(CONFIG_ENVIRONMENT, xstrdup(mkpath(\"%s/config\", git_dir)), 1);\n \ndiff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c\nindex b1d7f8f..29356d2 100644\n--- a/builtin-fetch--tool.c\n+++ b/builtin-fetch--tool.c\n@@ -246,7 +246,7 @@ static int fetch_native_store(FILE *fp,\n \tchar buffer[1024];\n \tint err = 0;\n \n-\tsigchain_push(SIGINT, remove_keep_on_signal);\n+\tsigchain_push_common(remove_keep_on_signal);\n \tatexit(remove_keep);\n \n \twhile (fgets(buffer, sizeof(buffer), stdin)) {\ndiff --git a/builtin-fetch.c b/builtin-fetch.c\nindex 8c86974..1e4a3d9 100644\n--- a/builtin-fetch.c\n+++ b/builtin-fetch.c\n@@ -673,7 +673,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)\n \t\tref_nr = j;\n \t}\n \n-\tsigchain_push(SIGINT, unlock_pack_on_signal);\n+\tsigchain_push_common(unlock_pack_on_signal);\n \tatexit(unlock_pack);\n \texit_code = do_fetch(transport,\n \t\t\tparse_fetch_refspec(ref_nr, refs), ref_nr);\ndiff --git a/diff.c b/diff.c\nindex dacd5d2..715709b 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1905,7 +1905,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name,\n \n \tif (!remove_tempfile_installed) {\n \t\tatexit(remove_tempfile);\n-\t\tsigchain_push(SIGINT, remove_tempfile_on_signal);\n+\t\tsigchain_push_common(remove_tempfile_on_signal);\n \t\tremove_tempfile_installed = 1;\n \t}\n \ndiff --git a/http-push.c b/http-push.c\nindex 4c92f80..178af75 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -2267,10 +2267,7 @@ int main(int argc, char **argv)\n \t\tgoto cleanup;\n \t}\n \n-\tsigchain_push(SIGINT, remove_locks_on_signal);\n-\tsigchain_push(SIGHUP, remove_locks_on_signal);\n-\tsigchain_push(SIGQUIT, remove_locks_on_signal);\n-\tsigchain_push(SIGTERM, remove_locks_on_signal);\n+\tsigchain_push_common(remove_locks_on_signal);\n \n \t/* Check whether the remote has server info files */\n \tremote->can_update_info_refs = 0;\ndiff --git a/lockfile.c b/lockfile.c\nindex 3cd57dc..021c337 100644\n--- a/lockfile.c\n+++ b/lockfile.c\n@@ -137,11 +137,7 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)\n \tlk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);\n \tif (0 <= lk->fd) {\n \t\tif (!lock_file_list) {\n-\t\t\tsigchain_push(SIGINT, remove_lock_file_on_signal);\n-\t\t\tsigchain_push(SIGHUP, remove_lock_file_on_signal);\n-\t\t\tsigchain_push(SIGTERM, remove_lock_file_on_signal);\n-\t\t\tsigchain_push(SIGQUIT, remove_lock_file_on_signal);\n-\t\t\tsigchain_push(SIGPIPE, remove_lock_file_on_signal);\n+\t\t\tsigchain_push_common(remove_lock_file_on_signal);\n \t\t\tatexit(remove_lock_file);\n \t\t}\n \t\tlk->owner = getpid();\ndiff --git a/sigchain.c b/sigchain.c\nindex a18d505..1118b99 100644\n--- a/sigchain.c\n+++ b/sigchain.c\n@@ -41,3 +41,12 @@ int sigchain_pop(int sig)\n \ts->n--;\n \treturn 0;\n }\n+\n+void sigchain_push_common(sigchain_fun f)\n+{\n+\tsigchain_push(SIGINT, f);\n+\tsigchain_push(SIGHUP, f);\n+\tsigchain_push(SIGTERM, f);\n+\tsigchain_push(SIGQUIT, f);\n+\tsigchain_push(SIGPIPE, f);\n+}\ndiff --git a/sigchain.h b/sigchain.h\nindex 254ebb0..618083b 100644\n--- a/sigchain.h\n+++ b/sigchain.h\n@@ -6,4 +6,6 @@ typedef void (*sigchain_fun)(int);\n int sigchain_push(int sig, sigchain_fun f);\n int sigchain_pop(int sig);\n \n+void sigchain_push_common(sigchain_fun f);\n+\n #endif /* SIGCHAIN_H */\n-- \n1.6.1.403.g6c435\n"},{"id":"101505","messageId":"20090122060328.GE30133@coredump.intra.peff.net","threadId":"17298","inReplyTo":"20090122042643.GB31427@coredump.intra.peff.net","subject":"[PATCH v2 5/5] pager: do wait_for_pager on signal death","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-01-22T06:03:28Z","receivedAt":"2009-01-22T06:03:28Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Since ea27a18 (spawn pager via run_command interface), the\noriginal git process actually does git work, and the pager\nis a child process (actually, on Windows it has always been\nthat way, since Windows lacks fork). After spawning the\npager, we register an atexit() handler that waits for the\npager to finish.\n\nUnfortunately, that handler does not always run. In\nparticular, if git is killed by a signal, then we exit\nimmediately. The calling shell then thinks that git is done;\nhowever, the pager is still trying to run and impact the\nterminal. The result can be seen by running a long git\nprocess with a pager (e.g., \"git log -p\") and hitting ^C.\nDepending on your config, you should see the shell prompt,\nbut pressing a key causes the pager to do any terminal\nde-initialization sequence.\n\nThis patch just intercepts any death-dealing signals and\nwaits for the pager before dying. Under typical less\nconfiguration, that means hitting ^C will cause git to stop\ngenerating output, but the pager will keep running.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nSame as before.\n\n pager.c |    9 +++++++++\n 1 files changed, 9 insertions(+), 0 deletions(-)\n\ndiff --git a/pager.c b/pager.c\nindex f19ddbc..4921843 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -1,5 +1,6 @@\n #include \"cache.h\"\n #include \"run-command.h\"\n+#include \"sigchain.h\"\n \n /*\n  * This is split up from the rest of git so that we can do\n@@ -38,6 +39,13 @@ static void wait_for_pager(void)\n \tfinish_command(&pager_process);\n }\n \n+static void wait_for_pager_signal(int signo)\n+{\n+\twait_for_pager();\n+\tsigchain_pop(signo);\n+\traise(signo);\n+}\n+\n void setup_pager(void)\n {\n \tconst char *pager = getenv(\"GIT_PAGER\");\n@@ -75,6 +83,7 @@ void setup_pager(void)\n \tclose(pager_process.in);\n \n \t/* this makes sure that the parent terminates after the pager */\n+\tsigchain_push_common(wait_for_pager_signal);\n \tatexit(wait_for_pager);\n }\n \n-- \n1.6.1.403.g6c435\n"},{"id":"101619","messageId":"7vr62u35i5.fsf@gitster.siamese.dyndns.org","threadId":"17298","inReplyTo":"200901212321.50526.bss@iguanasuicide.net","subject":"Re: What's cooking in git.git (Jan 2009, #05; Wed, 21)","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-01-23T06:23:30Z","receivedAt":"2009-01-23T06:23:30Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Boyd Stephen Smith Jr.\" <bss@iguanasuicide.net> writes:\n\n>>* js/diff-color-words (Tue Jan 20 21:46:57 2009 -0600) 8 commits\n>> + color-words: Support diff.wordregex config option\n>> + color-words: make regex configurable via attributes\n>> + color-words: expand docs with precise semantics\n>> + color-words: enable REG_NEWLINE to help user\n>> + color-words: take an optional regular expression describing words\n>> + color-words: change algorithm to allow for 0-character word\n>>   boundaries\n>> + color-words: refactor word splitting and use ALLOC_GROW()\n>> + Add color_fwrite_lines(), a function coloring each line\n>>   individually\n>\n> I think my patch in \n> http://thread.gmane.org/gmane.comp.version-control.git/106567 should be \n> applied to the top of this.\n\nThanks.\n"},{"id":"102070","messageId":"200901261943.23058.bss@iguanasuicide.net","threadId":"17298","inReplyTo":"200901212321.50526.bss@iguanasuicide.net","subject":"Re: What's cooking in git.git (Jan 2009, #05; Wed, 21)","fromName":"Boyd Stephen Smith Jr.","fromEmail":"bss@iguanasuicide.net","sentAt":"2009-01-27T01:43:17Z","receivedAt":"2009-01-27T01:43:17Z","isPatch":false,"sender":{"key":"bss@iguanasuicide.net","avatar":"https://gravatar.com/avatar/84b95eeff194b816c1568b1339e63e4b229825298664a9037b9f1ec713ead1e3?d=mp&s=160"},"body":"On Wednesday 21 January 2009, \"Boyd Stephen Smith Jr.\" \n<bss@iguanasuicide.net> wrote about 'Re: What's cooking in git.git (Jan \n2009, #05; Wed, 21)':\n>On Wednesday 21 January 2009, Junio C Hamano <gitster@pobox.com> wrote\n>about 'What's cooking in git.git (Jan 2009, #05; Wed, 21)':\n>>* js/notes (Tue Jan 13 20:57:16 2009 +0100) 6 commits\n>>\n>>It would be nice to hear a real world success story using the notes\n>>mechanism before casting this design in stone.\n>\n>I'll see if I can't try to put this through some paces over the week.\n\nYeah, that's not gonna happen.  I still want to play with this some, but \nthat's being pushed to the background, so I can't say when I'll really get \ntime to test it.\n-- \nBoyd Stephen Smith Jr.                     ,= ,-_-. =. \nbss@iguanasuicide.net                     ((_/)o o(\\_))\nICQ: 514984 YM/AIM: DaTwinkDaddy           `-'(. .)`-' \nhttp://iguanasuicide.net/                      \\_/     \n"},{"id":"102550","messageId":"20090130075552.GA27716@coredump.intra.peff.net","threadId":"17298","inReplyTo":"20090122060235.GC30133@coredump.intra.peff.net","subject":"Re: [PATCH v2 3/5] chain kill signals for cleanup functions","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-01-30T07:55:52Z","receivedAt":"2009-01-30T07:55:52Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jan 22, 2009 at 01:02:35AM -0500, Jeff King wrote:\n\n> diff --git a/test-sigchain.c b/test-sigchain.c\n> new file mode 100644\n> index 0000000..8747dea\n> --- /dev/null\n> +++ b/test-sigchain.c\n> [...]\n> +int main(int argc, char **argv) {\n> +\tsigchain_push(SIGINT, one);\n> +\tsigchain_push(SIGINT, two);\n> +\tsigchain_push(SIGINT, three);\n> +\traise(SIGINT);\n> +\treturn 0;\n> +}\n\nThe signal-handling test was failing on my Solaris auto-build. After\nmuch painful debugging, it seems that when running without a controlling\nterminal (such as under cron), the signal handler for terminal related\nsignals (including SIGINT) is initialized to SIG_IGN. Thus after popping\nall of our signal handlers, we restore the SIG_IGN, the program is _not_\nkilled by the signal, and the test fails.\n\nOne fix would be to just \"signal(SIGINT, SIG_DFL)\" at the top. But I\nthink it makes the test cleaner to just switch to a more reliable\nsignal. The patch would look something like what is below. But I need to\nknow what exit code Windows generates for SIGTERM. Johannes?\n\n---\ndiff --git a/t/t0005-signals.sh b/t/t0005-signals.sh\nindex 9707af7..09f855a 100755\n--- a/t/t0005-signals.sh\n+++ b/t/t0005-signals.sh\n@@ -12,7 +12,7 @@ EOF\n test_expect_success 'sigchain works' '\n \ttest-sigchain >actual\n \tcase \"$?\" in\n-\t130) true ;; # POSIX w/ SIGINT=2\n+\t143) true ;; # POSIX w/ SIGTERM=15\n \t  3) true ;; # Windows\n \t  *) false ;;\n \tesac &&\ndiff --git a/test-sigchain.c b/test-sigchain.c\nindex 8747dea..42db234 100644\n--- a/test-sigchain.c\n+++ b/test-sigchain.c\n@@ -14,9 +14,9 @@ X(three)\n #undef X\n \n int main(int argc, char **argv) {\n-\tsigchain_push(SIGINT, one);\n-\tsigchain_push(SIGINT, two);\n-\tsigchain_push(SIGINT, three);\n-\traise(SIGINT);\n+\tsigchain_push(SIGTERM, one);\n+\tsigchain_push(SIGTERM, two);\n+\tsigchain_push(SIGTERM, three);\n+\traise(SIGTERM);\n \treturn 0;\n }\n"},{"id":"102551","messageId":"4982B68C.2070207@viscovery.net","threadId":"17298","inReplyTo":"20090130075552.GA27716@coredump.intra.peff.net","subject":"Re: [PATCH v2 3/5] chain kill signals for cleanup functions","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2009-01-30T08:13:00Z","receivedAt":"2009-01-30T08:13:00Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Jeff King schrieb:\n> One fix would be to just \"signal(SIGINT, SIG_DFL)\" at the top. But I\n> think it makes the test cleaner to just switch to a more reliable\n> signal. The patch would look something like what is below. But I need to\n> know what exit code Windows generates for SIGTERM. Johannes?\n\nThe same as with SIGINT: 3.\n\n-- Hannes\n"},{"id":"102552","messageId":"20090130082101.GA28809@coredump.intra.peff.net","threadId":"17298","inReplyTo":"4982B68C.2070207@viscovery.net","subject":"Re: [PATCH v2 3/5] chain kill signals for cleanup functions","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-01-30T08:21:01Z","receivedAt":"2009-01-30T08:21:01Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jan 30, 2009 at 09:13:00AM +0100, Johannes Sixt wrote:\n\n> Jeff King schrieb:\n> > One fix would be to just \"signal(SIGINT, SIG_DFL)\" at the top. But I\n> > think it makes the test cleaner to just switch to a more reliable\n> > signal. The patch would look something like what is below. But I need to\n> > know what exit code Windows generates for SIGTERM. Johannes?\n> \n> The same as with SIGINT: 3.\n\nHmm. Clever.\n\nJunio, can you apply this on top of the jk/signal-cleanup topic?\n\n-- >8 --\nSubject: [PATCH] t0005: use SIGTERM for sigchain test\n\nThe signal tests consists of checking that each of our\nhandlers is executed, and that the test program was killed\nby the final signal. We arbitrarily used SIGINT as the kill\nsignal.\n\nHowever, some platforms (notably Solaris) will default\nSIGINT to SIG_IGN if there is no controlling terminal. In\nthat case, we don't end up killing the program with the\nfinal signal and the test fails.\n\nThis is a problem since the test script should not depend\non outside factors; let's use SIGTERM instead, which should\nbehave consistently.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n t/t0005-signals.sh |    2 +-\n test-sigchain.c    |    8 ++++----\n 2 files changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/t/t0005-signals.sh b/t/t0005-signals.sh\nindex 9707af7..09f855a 100755\n--- a/t/t0005-signals.sh\n+++ b/t/t0005-signals.sh\n@@ -12,7 +12,7 @@ EOF\n test_expect_success 'sigchain works' '\n \ttest-sigchain >actual\n \tcase \"$?\" in\n-\t130) true ;; # POSIX w/ SIGINT=2\n+\t143) true ;; # POSIX w/ SIGTERM=15\n \t  3) true ;; # Windows\n \t  *) false ;;\n \tesac &&\ndiff --git a/test-sigchain.c b/test-sigchain.c\nindex 8747dea..42db234 100644\n--- a/test-sigchain.c\n+++ b/test-sigchain.c\n@@ -14,9 +14,9 @@ X(three)\n #undef X\n \n int main(int argc, char **argv) {\n-\tsigchain_push(SIGINT, one);\n-\tsigchain_push(SIGINT, two);\n-\tsigchain_push(SIGINT, three);\n-\traise(SIGINT);\n+\tsigchain_push(SIGTERM, one);\n+\tsigchain_push(SIGTERM, two);\n+\tsigchain_push(SIGTERM, three);\n+\traise(SIGTERM);\n \treturn 0;\n }\n-- \n1.6.1.2.420.ga6a64.dirty\n"},{"id":"102664","messageId":"7vd4e49v48.fsf@gitster.siamese.dyndns.org","threadId":"17298","inReplyTo":"20090130082101.GA28809@coredump.intra.peff.net","subject":"Re: [PATCH v2 3/5] chain kill signals for cleanup functions","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-01-31T00:28:39Z","receivedAt":"2009-01-31T00:28:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Fri, Jan 30, 2009 at 09:13:00AM +0100, Johannes Sixt wrote:\n>\n>> Jeff King schrieb:\n>> > One fix would be to just \"signal(SIGINT, SIG_DFL)\" at the top. But I\n>> > think it makes the test cleaner to just switch to a more reliable\n>> > signal. The patch would look something like what is below. But I need to\n>> > know what exit code Windows generates for SIGTERM. Johannes?\n>> \n>> The same as with SIGINT: 3.\n>\n> Hmm. Clever.\n>\n> Junio, can you apply this on top of the jk/signal-cleanup topic?\n\nWill do, but I've been sick today, haven't caught up with the list\ntraffic, and I do not think I'll be reading my mails for the rest of the\nday either.  It may take some time for it to appear in the public\nrepositories.\n\nThanks.\n"},{"id":"102675","messageId":"20090131014420.GA7696@sigill.intra.peff.net","threadId":"17298","inReplyTo":"7vd4e49v48.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH v2 3/5] chain kill signals for cleanup functions","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-01-31T01:44:20Z","receivedAt":"2009-01-31T01:44:20Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jan 30, 2009 at 04:28:39PM -0800, Junio C Hamano wrote:\n\n> > Hmm. Clever.\n> >\n> > Junio, can you apply this on top of the jk/signal-cleanup topic?\n> \n> Will do, but I've been sick today, haven't caught up with the list\n> traffic, and I do not think I'll be reading my mails for the rest of the\n> day either.  It may take some time for it to appear in the public\n> repositories.\n\nNo problem. It really is a fix for a false negative in the test, not any\nactual git bug, so no rush.\n\nNow I'm off to go make fun of you, buried deep in a thread where you\nwon't see it.\n\n-Peff\n"},{"id":"102678","messageId":"1233384354.10045.170.camel@maia.lan","threadId":"17298","inReplyTo":"alpine.DEB.1.00.0901220606040.3586@pacific.mpi-cbg.de","subject":"Re: What's cooking in git.git (Jan 2009, #05; Wed, 21)","fromName":"Sam Vilain","fromEmail":"sam@vilain.net","sentAt":"2009-01-31T06:45:54Z","receivedAt":"2009-01-31T06:45:54Z","isPatch":false,"sender":{"key":"sam@vilain.net","avatar":"https://gravatar.com/avatar/8fc840ca854dbf6f7065b4335e3b934951c1dca3b11db688e95e471901f8f4a8?d=mp&s=160"},"body":"On Thu, 2009-01-22 at 06:13 +0100, Johannes Schindelin wrote:\n> > It would be nice to hear a real world success story using the notes\n> > mechanism before casting this design in stone.\n> \n> I'd like to have some profiling done before that.  For example, I am still \n> a bit unsure how the things would perform with a 50-deep delta chain for \n> a notes tree having 50,000+ notes in it (which I think will not be all \n> that unreasonable for a medium-sized project that stores bug-tracking \n> information in the notes).\n\nIs there any reason why the split has to be cast in stone at all?\n\nie, the code could just scan the root tree of the branch, and\nprogressively descend into sub-trees based on a partial match of the\nobject for which the note is to be found.  If you find a partial name\nthen you expect that it is a tree and descend into it and scan for the\nrest.  If you find a complete name then you expect that it is a blob and\nopen it.  If it turns out to be a tree then there are multiple notes for\nthat commit.  Then I think you get the best of both worlds; you can\nstart with a simple flat structure and then later someone can come along\nand make it split it when there are more than N entries in the root tree\n(where N is determined from profiling etc).\n\nThere are two practical applications I could use this for straight away\nfor perl.git, and I think that they would be important use cases.\n\nOne would be to allow grafts to be noted.  These might want to live in a\ndifferent place to refs/notes/commits, like refs/notes/grafts, to avoid\nperformance issues and to recognise they are a different type of data.\nA second would be for commit header information - particularly the\nauthor field and commit description - to be amended.  I think this all\nbelongs under refs/notes/commits.  These are in essence, historical\ncorrections that don't need to alter the tree.\n\nThe idea of making it allow a union merge seems relatively workable, I\nthink for simplicity and flexibility that the contents of the note\nshould be considered to be format-patch output (except without the diff\nof course).  So union-ish, more like a RFC822-aware merge of mail\nmessages.\n\neg, say the contents of the note are:\n\n  Some text\n\n=> appends \"Some text\" to the note as currently implemented\n\n  Subject: Blah blah\n\n  Blah blah blah\n\n=> _replaces_ commit message and body, as if it had been committed\n   with the above message\n\n  From: Sam Vilain <sam@vilain.net>\n  Date: Thu, 22 Jan 2009 06:13:01 +1300\n\n  Blah blah blah\n\n=> replaces 'author' line in commit.  \"Blah blah blah\" appended to\n   commit body.\n\nSound sane?\n\nSam.\n"},{"id":"102679","messageId":"20090131065049.GA3130@coredump.intra.peff.net","threadId":"17298","inReplyTo":"20090131014420.GA7696@sigill.intra.peff.net","subject":"Re: [PATCH v2 3/5] chain kill signals for cleanup functions","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-01-31T06:50:49Z","receivedAt":"2009-01-31T06:50:49Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jan 30, 2009 at 08:44:20PM -0500, Jeff King wrote:\n\n> > Will do, but I've been sick today, haven't caught up with the list\n> > traffic, and I do not think I'll be reading my mails for the rest of the\n> > day either.  It may take some time for it to appear in the public\n> > repositories.\n> [...]\n> Now I'm off to go make fun of you, buried deep in a thread where you\n> won't see it.\n\nHmm, reading that again, it sounds mean, and I didn't mean it that way.\nI should have put a \";P\" at the end. What I meant was \"while the cat is\naway, the mice will play\" (i.e., make mischief while our benevolent\ndictator is out of commission).\n\nHope you feel better soon.\n\n-Peff\n"},{"id":"102686","messageId":"20090131073640.GF3033@coredump.intra.peff.net","threadId":"17298","inReplyTo":"1233384354.10045.170.camel@maia.lan","subject":"Re: What's cooking in git.git (Jan 2009, #05; Wed, 21)","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-01-31T07:36:40Z","receivedAt":"2009-01-31T07:36:40Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Jan 31, 2009 at 07:45:54PM +1300, Sam Vilain wrote:\n\n> Is there any reason why the split has to be cast in stone at all?\n> \n> ie, the code could just scan the root tree of the branch, and\n> progressively descend into sub-trees based on a partial match of the\n> object for which the note is to be found.  If you find a partial name\n> then you expect that it is a tree and descend into it and scan for the\n> rest.  If you find a complete name then you expect that it is a blob and\n> open it.  If it turns out to be a tree then there are multiple notes for\n> that commit.  Then I think you get the best of both worlds; you can\n> start with a simple flat structure and then later someone can come along\n> and make it split it when there are more than N entries in the root tree\n> (where N is determined from profiling etc).\n\nActually, lookup is even easier than that: we iterate through the entire\ntree recursively and add everything to a flat hash. So we really don't\ncare there what the layout is like (just take the first 40 characters of\nany directory name as a hash).\n\nBut it violates the usual git principle of \"content has a unique name\".\nWhat happens when I add \"a/b\" and you add \"ab\"? A dumb merge will let\nboth co-exist, but which one do you return for lookup?\n\n> One would be to allow grafts to be noted.  These might want to live in a\n> different place to refs/notes/commits, like refs/notes/grafts, to avoid\n> performance issues and to recognise they are a different type of data.\n> A second would be for commit header information - particularly the\n> author field and commit description - to be amended.  I think this all\n> belongs under refs/notes/commits.  These are in essence, historical\n> corrections that don't need to alter the tree.\n\nI agree that there should be multiple note hierarchies, and multiple\nkeys within each hierarchy. I have posted some thoughts on that before\n(and you should be able to find them searching for \"notes\" in the list\narchive), but unfortunately I have not had time to sit down and really\nwork out a notes implementation that matches what I posted (which I\ndon't think is that far from Dscho's work in next).\n\nAnd I think what you are proposing (here and in the rest of your\nmessage) is that certain notes hierarchies may have particular formats\nand semantics. And that sounds reasonable to me.\n\n-Peff\n"},{"id":"102742","messageId":"7vab96zzo0.fsf@gitster.siamese.dyndns.org","threadId":"17298","inReplyTo":"20090131065049.GA3130@coredump.intra.peff.net","subject":"Re: [PATCH v2 3/5] chain kill signals for cleanup functions","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-02-01T01:58:07Z","receivedAt":"2009-02-01T01:58:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Fri, Jan 30, 2009 at 08:44:20PM -0500, Jeff King wrote:\n>\n>> > Will do, but I've been sick today, haven't caught up with the list\n>> > traffic, and I do not think I'll be reading my mails for the rest of the\n>> > day either.  It may take some time for it to appear in the public\n>> > repositories.\n>> [...]\n>> Now I'm off to go make fun of you, buried deep in a thread where you\n>> won't see it.\n>\n> Hmm, reading that again, it sounds mean, and I didn't mean it that way.\n> I should have put a \";P\" at the end. What I meant was \"while the cat is\n> away, the mice will play\" (i.e., make mischief while our benevolent\n> dictator is out of commission).\n>\n> Hope you feel better soon.\n\nThanks.  I guess not much happened on the list while I was sick in bed.\n\nI'll try to take a bit more rest and catch up tomorrow.\n"},{"id":"102747","messageId":"1233455960.17688.121.camel@maia.lan","threadId":"17298","inReplyTo":"20090131073640.GF3033@coredump.intra.peff.net","subject":"[PATCH] split notes [was: Re: What's cooking in git.git (Jan 2009, #05; Wed, 21)]","fromName":"Sam Vilain","fromEmail":"sam@vilain.net","sentAt":"2009-02-01T02:39:20Z","receivedAt":"2009-02-01T02:39:20Z","isPatch":true,"sender":{"key":"sam@vilain.net","avatar":"https://gravatar.com/avatar/8fc840ca854dbf6f7065b4335e3b934951c1dca3b11db688e95e471901f8f4a8?d=mp&s=160"},"body":"On Sat, 2009-01-31 at 02:36 -0500, Jeff King wrote:\n> Actually, lookup is even easier than that: we iterate through the entire\n> tree recursively and add everything to a flat hash. So we really don't\n> care there what the layout is like (just take the first 40 characters of\n> any directory name as a hash).\n\nSure, but if you do want to scale to a hundred thousand notes, then I\nthink it would pay to have a plan for making it lazy as required.  ie,\nif a run just wants notes for 20 commits and there are 256 sub-trees\nthen only read 20 of them.  Doesn't matter if it's not implemented\ninitially of course, so long as the on-disk format is supported by the\ntools in the first release they will be backward compatible.  And it's\nnot that complicated; see attached.\n\n> But it violates the usual git principle of \"content has a unique name\".\n> What happens when I add \"a/b\" and you add \"ab\"? A dumb merge will let\n> both co-exist, but which one do you return for lookup?\n\nIt should only be tools adding to it, the trees shouldn't be modified\ndirectly by users.  In the below patch I make these all get deleted on\n'git notes edit'.\n\nDepending on the semantics of the notes, it might not be an error to\nhave multiple notes for a commit.  In my patch this is \"tolerated\" to\nsome extent but not supported by git-notes.sh porcelain yet.\n\n> I agree that there should be multiple note hierarchies, and multiple\n> keys within each hierarchy. I have posted some thoughts on that before\n> (and you should be able to find them searching for \"notes\" in the list\n> archive), but unfortunately I have not had time to sit down and really\n> work out a notes implementation that matches what I posted (which I\n> don't think is that far from Dscho's work in next).\n\nI had a brief look and couldn't find it, this was about the best one I\nfound from you in terms of links to previous discussions;\nhttp://kerneltrap.org/mailarchive/git/2008/12/16/4427794 If there's\nanother thread you'd like me to read please fish it out and respond!\nThe more messages we have linking to the previous discussions the\nbetter :).\n\n> And I think what you are proposing (here and in the rest of your\n> message) is that certain notes hierarchies may have particular formats\n> and semantics. And that sounds reasonable to me.\n\nYes that was one part of it.  But also make a convention that the\n'commits' notes, ie the default ones, an RFC822 message if they begin\nwith \"known\" headers.  Then porcelain such as log can inject them into\nthe fields at the appropriate point.\n\nAnyway, without further ado here's the XX/XXXX split patch.\n\nSubject: [PATCH] git-notes: allow for arbitrary split of entries into sub-trees\n\nIt might later turn out for performance reasons that a single tree for\nnotes will not be sufficient.  While this does not solve the\nperformance problem as it still loads the entire lot of notes into a\nhash at start-up, it does mean that such a change does not have to\nworry about backward compatibility with git versions that don't yet\nsupport it.\n\nSigned-off-by: Sam Vilain <sam@vilain.net>\n---\n git-notes.sh     |   45 ++++++++++++++++++++++++++++++++++++++-------\n notes.c          |   39 ++++++++++++++++++++++++++++++++-------\n t/t3301-notes.sh |   11 +++++++++++\n 3 files changed, 81 insertions(+), 14 deletions(-)\n\ndiff --git a/git-notes.sh b/git-notes.sh\nindex bfdbaa8..e07499f 100755\n--- a/git-notes.sh\n+++ b/git-notes.sh\n@@ -10,15 +10,35 @@ ACTION=\"$1\"; shift\n \n test -z \"$GIT_NOTES_REF\" && GIT_NOTES_REF=\"$(git config core.notesref)\"\n test -z \"$GIT_NOTES_REF\" && GIT_NOTES_REF=\"refs/notes/commits\"\n+export GIT_NOTES_REF\n \n COMMIT=$(git rev-parse --verify --default HEAD \"$@\") ||\n die \"Invalid commit: $@\"\n+NOTES_PATH=$COMMIT\n+case \"$GIT_NOTES_SPLIT\" in\n+\t[1-9]|[1-4][0-9])\n+\t\tNOTES_PATH=$( echo $COMMIT | perl -pe 's{^(.{'$GIT_NOTES_SPLIT'})}{$1/}' )\n+\t\t;;\n+esac\n \n MESSAGE=\"$GIT_DIR\"/new-notes-$COMMIT\n trap '\n \ttest -f \"$MESSAGE\" && rm \"$MESSAGE\"\n ' 0\n \n+show_note() {\n+\tCOMMIT=$1\n+\tNOTE_PATH=$( git ls-tree --name-only -r $GIT_NOTES_REF | perl -nle '\n+\t\t$x = $_; s{/}{}g;\n+\t\tif (m{'$COMMIT'}) {\n+\t\t\tprint $x;\n+\t\t\texit;\n+\t\t}\n+\t' )\n+\t[ -n \"$NOTE_PATH\" ] &&\n+\t\tgit cat-file blob $GIT_NOTES_REF:$NOTE_PATH\n+}\n+\n case \"$ACTION\" in\n edit)\n \tGIT_NOTES_REF= git log -1 $COMMIT | sed \"s/^/#/\" > \"$MESSAGE\"\n@@ -32,7 +52,7 @@ edit)\n \telse\n \t\tPARENT=\"-p $CURRENT_HEAD\"\n \t\tgit read-tree \"$GIT_NOTES_REF\" || die \"Could not read index\"\n-\t\tgit cat-file blob :$COMMIT >> \"$MESSAGE\" 2> /dev/null\n+\t\tshow_note $COMMIT >> \"$MESSAGE\"\n \tfi\n \n \t${VISUAL:-${EDITOR:-vi}} \"$MESSAGE\"\n@@ -42,15 +62,26 @@ edit)\n \tif [ -s \"$MESSAGE\" ]; then\n \t\tBLOB=$(git hash-object -w \"$MESSAGE\") ||\n \t\t\tdie \"Could not write into object database\"\n-\t\tgit update-index --add --cacheinfo 0644 $BLOB $COMMIT ||\n+\t\tgit update-index --add --cacheinfo 0644 $BLOB $NOTES_PATH ||\n \t\t\tdie \"Could not write index\"\n \telse\n-\t\ttest -z \"$CURRENT_HEAD\" &&\n-\t\t\tdie \"Will not initialise with empty tree\"\n-\t\tgit update-index --force-remove $COMMIT ||\n-\t\t\tdie \"Could not update index\"\n+\t\tNOTES_PATH=dummy\n \tfi\n \n+\tgit ls-files | perl -nle '\n+\t\t$x = $_; s{/}{}g;\n+\t\tif (m{'$COMMIT'} and $x ne q{'$NOTES_PATH'}) {\n+\t\t\tprint $x;\n+\t\t}' |\n+\t\twhile read path\n+\t\t\tdo\n+\t\t\t\tgit update-index --force-remove $path ||\n+\t\t\t    \t\tdie \"Could not update index\"\n+\t\t\tdone\n+\t\n+\t[ -z \"$(git ls-files)\" -a -z \"$CURRENT_HEAD\" ] &&\n+\t\tdie \"Will not initialise with empty tree\"\n+\n \tTREE=$(git write-tree) || die \"Could not write tree\"\n \tNEW_HEAD=$(echo Annotate $COMMIT | git commit-tree $TREE $PARENT) ||\n \t\tdie \"Could not annotate\"\n@@ -58,7 +89,7 @@ edit)\n \t\t\"$GIT_NOTES_REF\" $NEW_HEAD $CURRENT_HEAD\n ;;\n show)\n-\tgit show \"$GIT_NOTES_REF\":$COMMIT\n+\tshow_note $COMMIT\n ;;\n *)\n \tusage\ndiff --git a/notes.c b/notes.c\nindex bd73784..d763b50 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -70,28 +70,53 @@ static void add_entry(const unsigned char *commit_sha1,\n \thashcpy(hash_map.entries[index].notes_sha1, notes_sha1);\n }\n \n-static void initialize_hash_map(const char *notes_ref_name)\n+static void initialize_hash_map_recursive(const char *tree_sha1, const char *path)\n {\n \tunsigned char sha1[20], commit_sha1[20];\n+\tunsigned char path_combined[40];\n \tunsigned mode;\n \tstruct tree_desc desc;\n \tstruct name_entry entry;\n+\tint length = strlen(path);\n+\tint length_combined;\n \tvoid *buf;\n \n-\tif (!notes_ref_name || read_ref(notes_ref_name, commit_sha1) ||\n-\t    get_tree_entry(commit_sha1, \"\", sha1, &mode))\n+\tstrcpy(path_combined, path);\n+\n+\tif (get_tree_entry(tree_sha1, \"\", sha1, &mode))\n \t\treturn;\n \n \tbuf = fill_tree_descriptor(&desc, sha1);\n \tif (!buf)\n-\t\tdie(\"Could not read %s for notes-index\", sha1_to_hex(sha1));\n+\t\tdie(\"Could not read %s for notes-index\", sha1_to_hex(tree_sha1));\n+\n+\twhile (tree_entry(&desc, &entry)) {\n+\t\tlength_combined = length + strlen(entry.path);\n+\t\tif (length_combined >= 40) {\n+\t\t\tstrncpy(path_combined + length, entry.path,\n+\t\t\t\t41 - length);\n+\t\t\tif (!get_sha1(path_combined, commit_sha1))\n+\t\t\t\tadd_entry(commit_sha1, entry.sha1);\n+\t\t}\n+\t\telse {\n+\t\t\tstrcpy(path_combined + length, entry.path);\n+\t\t\tinitialize_hash_map_recursive(entry.sha1, path_combined);\n+\t\t}\n+\t}\n \n-\twhile (tree_entry(&desc, &entry))\n-\t\tif (!get_sha1(entry.path, commit_sha1))\n-\t\t\tadd_entry(commit_sha1, entry.sha1);\n \tfree(buf);\n }\n \n+static void initialize_hash_map(const char *notes_ref_name)\n+{\n+\tunsigned char commit_sha1[20];\n+\n+\tif (!notes_ref_name || read_ref(notes_ref_name, commit_sha1))\n+\t\treturn;\n+\n+\tinitialize_hash_map_recursive( commit_sha1, \"\" );\n+}\n+\n static unsigned char *lookup_notes(const unsigned char *commit_sha1)\n {\n \tint index;\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex 9393a25..3734b55 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -92,4 +92,15 @@ test_expect_success 'show multi-line notes' '\n \ttest_cmp expect-multiline output\n '\n \n+test_expect_success 'create split notes tree' '\n+\t: > a4 &&\n+\tgit add a4 &&\n+\ttest_tick &&\n+\tgit commit -m 4th &&\n+\tMSG=\"b4\" GIT_NOTES_SPLIT=2 git notes edit &&\n+\t[ \"$(git notes show)\" = \"b4\" ] &&\n+\t[ -n \"$(git ls-tree --name-only -r refs/notes/commits | grep /)\" ] &&\n+\t[ -n \"$(git log -1 | grep Notes:)\" ]\n+'\n+\n test_done\n-- \ndebian.1.5.6.1\n"},{"id":"102748","messageId":"1233455960.17688.122.camel@maia.lan","threadId":"17298","inReplyTo":"20090131073640.GF3033@coredump.intra.peff.net","subject":"[PATCH] split notes [was: Re: What's cooking in git.git (Jan 2009, #05; Wed, 21)]","fromName":"Sam Vilain","fromEmail":"sam@vilain.net","sentAt":"2009-02-01T03:09:20Z","receivedAt":"2009-02-01T03:09:20Z","isPatch":true,"sender":{"key":"sam@vilain.net","avatar":"https://gravatar.com/avatar/8fc840ca854dbf6f7065b4335e3b934951c1dca3b11db688e95e471901f8f4a8?d=mp&s=160"},"body":"[re-sent with correct envelope from so hopefully it will get to the\nlist this time]\n\nOn Sat, 2009-01-31 at 02:36 -0500, Jeff King wrote:\n> Actually, lookup is even easier than that: we iterate through the entire\n> tree recursively and add everything to a flat hash. So we really don't\n> care there what the layout is like (just take the first 40 characters of\n> any directory name as a hash).\n\nSure, but if you do want to scale to a hundred thousand notes, then I\nthink it would pay to have a plan for making it lazy as required.  ie,\nif a run just wants notes for 20 commits and there are 256 sub-trees\nthen only read 20 of them.  Doesn't matter if it's not implemented\ninitially of course, so long as the on-disk format is supported by the\ntools in the first release they will be backward compatible.  And it's\nnot that complicated; see attached.\n\n> But it violates the usual git principle of \"content has a unique name\".\n> What happens when I add \"a/b\" and you add \"ab\"? A dumb merge will let\n> both co-exist, but which one do you return for lookup?\n\nIt should only be tools adding to it, the trees shouldn't be modified\ndirectly by users.  In the below patch I make these all get deleted on\n'git notes edit'.\n\nDepending on the semantics of the notes, it might not be an error to\nhave multiple notes for a commit.  In my patch this is \"tolerated\" to\nsome extent but not supported by git-notes.sh porcelain yet.\n\n> I agree that there should be multiple note hierarchies, and multiple\n> keys within each hierarchy. I have posted some thoughts on that before\n> (and you should be able to find them searching for \"notes\" in the list\n> archive), but unfortunately I have not had time to sit down and really\n> work out a notes implementation that matches what I posted (which I\n> don't think is that far from Dscho's work in next).\n\nI had a brief look and couldn't find it, this was about the best one I\nfound from you in terms of links to previous discussions;\nhttp://kerneltrap.org/mailarchive/git/2008/12/16/4427794 If there's\nanother thread you'd like me to read please fish it out and respond!\nThe more messages we have linking to the previous discussions the\nbetter :).\n\n> And I think what you are proposing (here and in the rest of your\n> message) is that certain notes hierarchies may have particular formats\n> and semantics. And that sounds reasonable to me.\n\nYes that was one part of it.  But also make a convention that the\n'commits' notes, ie the default ones, an RFC822 message if they begin\nwith \"known\" headers.  Then porcelain such as log can inject them into\nthe fields at the appropriate point.\n\nAnyway, without further ado here's the XX/XXXX split patch.\n\nSubject: [PATCH] git-notes: allow for arbitrary split of entries into sub-trees\n\nIt might later turn out for performance reasons that a single tree for\nnotes will not be sufficient.  While this does not solve the\nperformance problem as it still loads the entire lot of notes into a\nhash at start-up, it does mean that such a change does not have to\nworry about backward compatibility with git versions that don't yet\nsupport it.\n\nSigned-off-by: Sam Vilain <sam@vilain.net>\n---\n git-notes.sh     |   45 ++++++++++++++++++++++++++++++++++++++-------\n notes.c          |   39 ++++++++++++++++++++++++++++++++-------\n t/t3301-notes.sh |   11 +++++++++++\n 3 files changed, 81 insertions(+), 14 deletions(-)\n\ndiff --git a/git-notes.sh b/git-notes.sh\nindex bfdbaa8..e07499f 100755\n--- a/git-notes.sh\n+++ b/git-notes.sh\n@@ -10,15 +10,35 @@ ACTION=\"$1\"; shift\n \n test -z \"$GIT_NOTES_REF\" && GIT_NOTES_REF=\"$(git config core.notesref)\"\n test -z \"$GIT_NOTES_REF\" && GIT_NOTES_REF=\"refs/notes/commits\"\n+export GIT_NOTES_REF\n \n COMMIT=$(git rev-parse --verify --default HEAD \"$@\") ||\n die \"Invalid commit: $@\"\n+NOTES_PATH=$COMMIT\n+case \"$GIT_NOTES_SPLIT\" in\n+\t[1-9]|[1-4][0-9])\n+\t\tNOTES_PATH=$( echo $COMMIT | perl -pe 's{^(.{'$GIT_NOTES_SPLIT'})}{$1/}' )\n+\t\t;;\n+esac\n \n MESSAGE=\"$GIT_DIR\"/new-notes-$COMMIT\n trap '\n \ttest -f \"$MESSAGE\" && rm \"$MESSAGE\"\n ' 0\n \n+show_note() {\n+\tCOMMIT=$1\n+\tNOTE_PATH=$( git ls-tree --name-only -r $GIT_NOTES_REF | perl -nle '\n+\t\t$x = $_; s{/}{}g;\n+\t\tif (m{'$COMMIT'}) {\n+\t\t\tprint $x;\n+\t\t\texit;\n+\t\t}\n+\t' )\n+\t[ -n \"$NOTE_PATH\" ] &&\n+\t\tgit cat-file blob $GIT_NOTES_REF:$NOTE_PATH\n+}\n+\n case \"$ACTION\" in\n edit)\n \tGIT_NOTES_REF= git log -1 $COMMIT | sed \"s/^/#/\" > \"$MESSAGE\"\n@@ -32,7 +52,7 @@ edit)\n \telse\n \t\tPARENT=\"-p $CURRENT_HEAD\"\n \t\tgit read-tree \"$GIT_NOTES_REF\" || die \"Could not read index\"\n-\t\tgit cat-file blob :$COMMIT >> \"$MESSAGE\" 2> /dev/null\n+\t\tshow_note $COMMIT >> \"$MESSAGE\"\n \tfi\n \n \t${VISUAL:-${EDITOR:-vi}} \"$MESSAGE\"\n@@ -42,15 +62,26 @@ edit)\n \tif [ -s \"$MESSAGE\" ]; then\n \t\tBLOB=$(git hash-object -w \"$MESSAGE\") ||\n \t\t\tdie \"Could not write into object database\"\n-\t\tgit update-index --add --cacheinfo 0644 $BLOB $COMMIT ||\n+\t\tgit update-index --add --cacheinfo 0644 $BLOB $NOTES_PATH ||\n \t\t\tdie \"Could not write index\"\n \telse\n-\t\ttest -z \"$CURRENT_HEAD\" &&\n-\t\t\tdie \"Will not initialise with empty tree\"\n-\t\tgit update-index --force-remove $COMMIT ||\n-\t\t\tdie \"Could not update index\"\n+\t\tNOTES_PATH=dummy\n \tfi\n \n+\tgit ls-files | perl -nle '\n+\t\t$x = $_; s{/}{}g;\n+\t\tif (m{'$COMMIT'} and $x ne q{'$NOTES_PATH'}) {\n+\t\t\tprint $x;\n+\t\t}' |\n+\t\twhile read path\n+\t\t\tdo\n+\t\t\t\tgit update-index --force-remove $path ||\n+\t\t\t    \t\tdie \"Could not update index\"\n+\t\t\tdone\n+\t\n+\t[ -z \"$(git ls-files)\" -a -z \"$CURRENT_HEAD\" ] &&\n+\t\tdie \"Will not initialise with empty tree\"\n+\n \tTREE=$(git write-tree) || die \"Could not write tree\"\n \tNEW_HEAD=$(echo Annotate $COMMIT | git commit-tree $TREE $PARENT) ||\n \t\tdie \"Could not annotate\"\n@@ -58,7 +89,7 @@ edit)\n \t\t\"$GIT_NOTES_REF\" $NEW_HEAD $CURRENT_HEAD\n ;;\n show)\n-\tgit show \"$GIT_NOTES_REF\":$COMMIT\n+\tshow_note $COMMIT\n ;;\n *)\n \tusage\ndiff --git a/notes.c b/notes.c\nindex bd73784..d763b50 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -70,28 +70,53 @@ static void add_entry(const unsigned char *commit_sha1,\n \thashcpy(hash_map.entries[index].notes_sha1, notes_sha1);\n }\n \n-static void initialize_hash_map(const char *notes_ref_name)\n+static void initialize_hash_map_recursive(const char *tree_sha1, const char *path)\n {\n \tunsigned char sha1[20], commit_sha1[20];\n+\tunsigned char path_combined[40];\n \tunsigned mode;\n \tstruct tree_desc desc;\n \tstruct name_entry entry;\n+\tint length = strlen(path);\n+\tint length_combined;\n \tvoid *buf;\n \n-\tif (!notes_ref_name || read_ref(notes_ref_name, commit_sha1) ||\n-\t    get_tree_entry(commit_sha1, \"\", sha1, &mode))\n+\tstrcpy(path_combined, path);\n+\n+\tif (get_tree_entry(tree_sha1, \"\", sha1, &mode))\n \t\treturn;\n \n \tbuf = fill_tree_descriptor(&desc, sha1);\n \tif (!buf)\n-\t\tdie(\"Could not read %s for notes-index\", sha1_to_hex(sha1));\n+\t\tdie(\"Could not read %s for notes-index\", sha1_to_hex(tree_sha1));\n+\n+\twhile (tree_entry(&desc, &entry)) {\n+\t\tlength_combined = length + strlen(entry.path);\n+\t\tif (length_combined >= 40) {\n+\t\t\tstrncpy(path_combined + length, entry.path,\n+\t\t\t\t41 - length);\n+\t\t\tif (!get_sha1(path_combined, commit_sha1))\n+\t\t\t\tadd_entry(commit_sha1, entry.sha1);\n+\t\t}\n+\t\telse {\n+\t\t\tstrcpy(path_combined + length, entry.path);\n+\t\t\tinitialize_hash_map_recursive(entry.sha1, path_combined);\n+\t\t}\n+\t}\n \n-\twhile (tree_entry(&desc, &entry))\n-\t\tif (!get_sha1(entry.path, commit_sha1))\n-\t\t\tadd_entry(commit_sha1, entry.sha1);\n \tfree(buf);\n }\n \n+static void initialize_hash_map(const char *notes_ref_name)\n+{\n+\tunsigned char commit_sha1[20];\n+\n+\tif (!notes_ref_name || read_ref(notes_ref_name, commit_sha1))\n+\t\treturn;\n+\n+\tinitialize_hash_map_recursive( commit_sha1, \"\" );\n+}\n+\n static unsigned char *lookup_notes(const unsigned char *commit_sha1)\n {\n \tint index;\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex 9393a25..3734b55 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -92,4 +92,15 @@ test_expect_success 'show multi-line notes' '\n \ttest_cmp expect-multiline output\n '\n \n+test_expect_success 'create split notes tree' '\n+\t: > a4 &&\n+\tgit add a4 &&\n+\ttest_tick &&\n+\tgit commit -m 4th &&\n+\tMSG=\"b4\" GIT_NOTES_SPLIT=2 git notes edit &&\n+\t[ \"$(git notes show)\" = \"b4\" ] &&\n+\t[ -n \"$(git ls-tree --name-only -r refs/notes/commits | grep /)\" ] &&\n+\t[ -n \"$(git log -1 | grep Notes:)\" ]\n+'\n+\n test_done\n-- \ndebian.1.5.6.1\n"},{"id":"102752","messageId":"gm42vk$lg7$1@ger.gmane.org","threadId":"17298","inReplyTo":"1233455960.17688.122.camel@maia.lan","subject":"Re: [PATCH] split notes [was: Re: What's cooking in git.git (Jan 2009, #05; Wed, 21)]","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2009-02-01T12:01:55Z","receivedAt":"2009-02-01T12:01:55Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Sam Vilain wrote:\n\n>  test -z \"$GIT_NOTES_REF\" && GIT_NOTES_REF=\"$(git config core.notesref)\"\n>  test -z \"$GIT_NOTES_REF\" && GIT_NOTES_REF=\"refs/notes/commits\"\n\nGIT_NOTES_REF=$($(GIT_NOTES_REF:-$(git config core.notesref):-refs/notes/commits)\n\nOr something like that.\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"}]}