{"thread":{"id":"37715","subject":"[PATCH] git-completion.bash - avoid excruciatingly slow ref completion on Cygwin","startedAt":"2014-10-11T15:51:13Z","lastAt":"2014-10-18T14:47:31Z","messageCount":2,"participants":["Mark Levedahl","Tay Ray Chuan"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"250512","messageId":"1413042673-24052-1-git-send-email-mlevedahl@gmail.com","threadId":"37715","inReplyTo":null,"subject":"[PATCH] git-completion.bash - avoid excruciatingly slow ref completion on Cygwin","fromName":"Mark Levedahl","fromEmail":"mlevedahl@gmail.com","sentAt":"2014-10-11T15:51:13Z","receivedAt":"2014-10-11T15:51:13Z","isPatch":true,"sender":{"key":"mdl123@verizon.net","avatar":"https://avatars.githubusercontent.com/u/5302462?v=4"},"body":"$git checkout <tab> was taking about 3.5 seconds to respond on one\nrepository having four remotes with about 100 total refs (measured on\nCygwin).  All of the time was being claimed in \"git for-each-ref\" to do\nits work.  This working directory was created using git-new-workdir, and\nthus .git/refs and .git/packed-refs are both symlinks.  for-each-ref\noperates in a way that causes the .git/refs symlink to be resolved\nmultiple times for each ref in the repository, and Cygwin is especially\nslow in such operations.\n\nPatching refs.c to avoid repeatedly dereferencing the symlink reduced\nexecution time from about 3.5 seconds to about 1.1 seconds (but no\nimprovement on Linux), while an alternate approach of replacing the\nref-list expansion with a shell pipeline provides a larger improvement on\nCygwin and also improves Linux.  So, the shell pipeline approach is\nprovided here.\n\nRelevant timing results using the same repository on both Linux and\nCygwin:\n\nOn Cygwin:\n\n$ time git for-each-ref --format=\"%(refname:short)\" refs\n\nreal    0m3.523s\nuser    0m0.436s\nsys     0m2.733s\n\n$ time (cd \"$GIT_DIR\" ; cat packed-refs ; find refs/ -type f) \\\n\t2>/dev/null | sed -ne 's@^.*refs/@refs/@p' | sort | uniq\n\nreal    0m0.503s\nuser    0m0.307s\nsys     0m0.139s\n\nOn Linux (essentially the same hardware):\n\n$ time git for-each-ref --format=\"%(refname:short)\" refs\n\nreal    0m0.020s\nuser    0m0.006s\nsys     0m0.014s\n\n$ time (cd \"$GIT_DIR\" ; cat packed-refs ; find refs/ -type f) \\\n\t2>/dev/null | sed -ne 's@^.*refs/@refs/@p' | sort | uniq\n\nreal    0m0.012s\nuser    0m0.006s\nsys     0m0.005s\n\nSo, this is a win even on Linux, but more importantly it makes use of\ntab completion tolerable on Cygwin when symlinks are involved.\n\nSigned-off-by: Mark Levedahl <mlevedahl@gmail.com>\n---\n contrib/completion/git-completion.bash | 22 ++++++++++++++--------\n 1 file changed, 14 insertions(+), 8 deletions(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 965778e..62d976e 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -319,8 +319,9 @@ __git_heads ()\n {\n \tlocal dir=\"$(__gitdir)\"\n \tif [ -d \"$dir\" ]; then\n-\t\tgit --git-dir=\"$dir\" for-each-ref --format='%(refname:short)' \\\n-\t\t\trefs/heads\n+\t\t(cd \"$dir\" ; cat packed-refs ; find refs/heads -type f) 2>/dev/null |\n+\t\t\tsed -ne 's@^.*refs/heads/@@p' |\n+\t\t\tsort -u\n \t\treturn\n \tfi\n }\n@@ -329,8 +330,9 @@ __git_tags ()\n {\n \tlocal dir=\"$(__gitdir)\"\n \tif [ -d \"$dir\" ]; then\n-\t\tgit --git-dir=\"$dir\" for-each-ref --format='%(refname:short)' \\\n-\t\t\trefs/tags\n+\t\t(cd \"$dir\" ; cat packed-refs ; find refs/tags -type f) 2>/dev/null |\n+\t\t\tsed -ne 's@^.*refs/tags/@@p' |\n+\t\t\tsort -u\n \t\treturn\n \tfi\n }\n@@ -348,17 +350,21 @@ __git_refs ()\n \t\t\tformat=\"refname\"\n \t\t\trefs=\"${cur%/*}\"\n \t\t\ttrack=\"\"\n+\t\t\t(cd \"$dir\" ; cat packed-refs ; find refs/ -type f) 2>/dev/null |\n+\t\t\t\tsed -ne 's@^.*refs/@refs/@p' |\n+\t\t\t\tsort -u\n+\t\t\t\treturn\n \t\t\t;;\n \t\t*)\n \t\t\tfor i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do\n \t\t\t\tif [ -e \"$dir/$i\" ]; then echo $i; fi\n \t\t\tdone\n-\t\t\tformat=\"refname:short\"\n-\t\t\trefs=\"refs/tags refs/heads refs/remotes\"\n+\t\t\t(cd \"$dir\" ; cat packed-refs ; find refs/ -type f) 2>/dev/null |\n+\t\t\t\tsed -rne 's@^.*refs/(heads|remotes|tags)/@@p' |\n+\t\t\t\tsort -u\n+\t\t\t\treturn\n \t\t\t;;\n \t\tesac\n-\t\tgit --git-dir=\"$dir\" for-each-ref --format=\"%($format)\" \\\n-\t\t\t$refs\n \t\tif [ -n \"$track\" ]; then\n \t\t\t# employ the heuristic used by git checkout\n \t\t\t# Try to find a remote branch that matches the completion word\n-- \n2.1.2.2.0.14\n"},{"id":"250808","messageId":"CALUzUxpGXwu=sdh7MHjEcJzDRzZZkTR0NajU0hrBGCZZLGj2aw@mail.gmail.com","threadId":"37715","inReplyTo":"1413042673-24052-1-git-send-email-mlevedahl@gmail.com","subject":"Re: [PATCH] git-completion.bash - avoid excruciatingly slow ref completion on Cygwin","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2014-10-18T14:47:31Z","receivedAt":"2014-10-18T14:47:31Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"On Sat, Oct 11, 2014 at 11:51 PM, Mark Levedahl <mlevedahl@gmail.com> wrote:\n>\n> $git checkout <tab> was taking about 3.5 seconds to respond on one\n> repository having four remotes with about 100 total refs (measured on\n> Cygwin).  All of the time was being claimed in \"git for-each-ref\" to do\n> its work.  This working directory was created using git-new-workdir, and\n> thus .git/refs and .git/packed-refs are both symlinks.  for-each-ref\n> operates in a way that causes the .git/refs symlink to be resolved\n> multiple times for each ref in the repository, and Cygwin is especially\n> slow in such operations.\n\nThanks for looking into this.\n\n> Patching refs.c to avoid repeatedly dereferencing the symlink reduced\n> execution time from about 3.5 seconds to about 1.1 seconds (but no\n> improvement on Linux),\n\nThis makes your patch sound bad, but it isn't when one realises it is\nalready fast on Linux!\n\n> [snip]\n>\n> Relevant timing results using the same repository on both Linux and\n> Cygwin:\n\nFWIW, timings on msysgit:\n\n$ time git for-each-ref --format=\"%(refname:short)\" refs\n\nreal    0m8.799s\nuser    0m0.109s\nsys     0m0.250s\n\n$ time (cd \"$GIT_DIR\" ; cat packed-refs ; find refs/ -type f) \\\n        2>/dev/null | sed -ne 's@^.*refs/@refs/@p' | sort | uniq\n\nreal    0m3.406s\nuser    0m1.073s\nsys     0m2.398s\n\nso while your symlink-analysis might not accurately describe msysgit\n(I believe copies are made in place of a symlink), msysgit benefits\nfrom this too.\n\n-- \nCheers,\nRay Chuan\n\n-- \n-- \n*** Please reply-to-all at all times ***\n*** (do not pretend to know who is subscribed and who is not) ***\n*** Please avoid top-posting. ***\nThe msysGit Wiki is here: https://github.com/msysgit/msysgit/wiki - Github accounts are free.\n\nYou received this message because you are subscribed to the Google\nGroups \"msysGit\" group.\nTo post to this group, send email to msysgit@googlegroups.com\nTo unsubscribe from this group, send email to\nmsysgit+unsubscribe@googlegroups.com\nFor more options, and view previous threads, visit this group at\nhttp://groups.google.com/group/msysgit?hl=en_US?hl=en\n\n--- \nYou received this message because you are subscribed to the Google Groups \"Git for Windows\" group.\nTo unsubscribe from this group and stop receiving emails from it, send an email to msysgit+unsubscribe@googlegroups.com.\nFor more options, visit https://groups.google.com/d/optout.\n"}]}