{"thread":{"id":"35044","subject":"[PATCH v2 00/44] Ruby support","startedAt":"2013-09-28T22:03:25Z","lastAt":"2013-09-29T06:05:33Z","messageCount":62,"participants":["Felipe Contreras","Stefan Beller","Ramkumar Ramachandra"],"isPatch":true,"patchVersion":2,"patchTotal":44},"messages":[{"id":"228377","messageId":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":null,"subject":"[PATCH v2 00/44] Ruby support","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:25Z","receivedAt":"2013-09-28T22:03:25Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"This is a different patch series that tries a different approach.\n\nFirst, this series allows external scripts to also access libgit through Ruby\nbindings:\n\n  git ruby <<EOF\n  for_each_ref() do |name, sha1, flags|\n    puts \"%s: %s\" % [name, sha1_to_hex(sha1)]\n  end\n  EOF\n\nThis way third party script writers can write Ruby code that resembles Git's\ninternal C code, but in a much simpler way.\n\nSince the importance of the contrib area is fading, and it's important to move\nall Git commands to C, it's helpful to have tools that allows third parties to\nrewrite scripts that are easier to rewrite to C.\n\nIn addition, this would help Git developers to create quick prototypes and test\nnew code.\n\nFinally, and perhaps most importantly, Ruby scripts can be intermediaries\nbetween perl/shell scripts, and the final desired C code.\n\nI already argued this, but here is a clear demonstration.\n\nI took git-request-pull.sh, converted it to Ruby and incrementally moodified it\nuntil the resulting code was essentially what the code would look like in C,\nand then, finally, the rewrite to C, which follows the exact same logic of the\nRuby script.\n\nIn the progress of doing this, it was clear the shell script was buggy, so it\nwas fixed, and the C code needed fixes as well.\n\nIf we are serious about converting *all scripts* to C, clearly a powerful tool\nis needed, and Ruby bindings fit the bill.\n\nRuby scripts could be provided alongside the perl/shell alternatives, so people\nthat build with NO_RUBY=y don't loose any functionality. And eventually the\nRuby scripts (and originals) get removed once the conversion to C is completed.\n\nEven if Ruby is not used as an intermediary tool, and no official Git scripts are\nconverted to C this way, the bindings would still be useful to third parties.\n\nWho wouldn't benefit from adding this support?\n\nFelipe Contreras (44):\n  Add support for ruby commands\n  ruby: add support for internal ruby programs\n  request-pull: fix annotated tag check\n  request-pull: fix for specific branch\n  request-pull: use appropriate ref\n  request-pull: fix exact match checking\n  request-pull: trivial simplification\n  request-pull: t: trivial whitespace style fixes\n  request-pull: t: add missing cat\n  ruby: rewrite 'request-pull'\n  ruby: request-pull: rewrite perl script\n  ruby: request-pull: trivial simplifications\n  ruby: bind setup_git_directory()\n  ruby: bind dwim_ref()\n  ruby: request-pull: use native dwim_ref()\n  ruby: bind git_config()\n  ruby: request-pull: use native git_config()\n  ruby: bind read_ref()/peel_ref()\n  ruby: bind get_sha1()\n  ruby: request-pull: simplify tag fetching\n  ruby: request-pull: use get_sha1()\n  ruby: add Commit class\n  ruby: bind get_merge_bases()\n  ruby: request-pull: use get_merge_bases()\n  ruby: request-pull: trivial cleanups\n  ruby: bind remote and transport stuff\n  ruby: request-pull: use native remote and transport\n  ruby: bind find_unique_abbrev()\n  ruby: request-pull: use native commit info\n  ruby: bind read_sha1_file()\n  ruby: request-pull: use read_sha1_file()\n  revision: add missing include\n  shortlog: add missing declaration\n  shortlog: split builtin from common code\n  ruby: add RevInfo and DiffOptions\n  ruby: bind shortlog()\n  ruby: request-pull: use shortlog()\n  ruby: bind diff_tree_sha1()\n  ruby: bind log_tree_diff_flush()\n  ruby: request-pull: use native diff_tree stuff\n  ruby: request-pull: remove rescue block\n  ruby: remove GIT_PAGER from environment\n  ruby: add simpler option parser\n  request-pull: rewrite to C\n\n .gitignore              |   1 +\n Makefile                |  31 ++-\n builtin.h               |   1 +\n builtin/request-pull.c  | 275 +++++++++++++++++++++++\n builtin/shortlog.c      | 184 +--------------\n git-rb-setup.rb         | 109 +++++++++\n git-request-pull.sh     | 162 --------------\n git.c                   |   1 +\n revision.h              |   1 +\n ruby.c                  | 584 ++++++++++++++++++++++++++++++++++++++++++++++++\n shortlog.c              | 181 +++++++++++++++\n shortlog.h              |   8 +\n t/t10000-ruby.sh        | 233 +++++++++++++++++++\n t/t5150-request-pull.sh |  81 +++++--\n 14 files changed, 1492 insertions(+), 360 deletions(-)\n create mode 100644 builtin/request-pull.c\n create mode 100644 git-rb-setup.rb\n delete mode 100755 git-request-pull.sh\n create mode 100644 ruby.c\n create mode 100644 shortlog.c\n create mode 100755 t/t10000-ruby.sh\n\n-- \n1.8.4-fc\n"},{"id":"228378","messageId":"1380405849-13000-2-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 01/44] Add support for ruby commands","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:26Z","receivedAt":"2013-09-28T22:03:26Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    With this support, third parties would be able to write scripts more easily and\n    access libgit facilities.\n    \n    Also, it could help developers by allowing easier prototyping\n    \n      git ruby > actual <<EOF &&\n      for_each_ref() do |name, sha1, flags|\n        puts \"%s: %s\" % [name, sha1_to_hex(sha1)]\n      end\n\n .gitignore       |  1 +\n Makefile         | 19 ++++++++++++++++++-\n git-rb-setup.rb  | 11 +++++++++++\n ruby.c           | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n t/t10000-ruby.sh | 42 ++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 128 insertions(+), 1 deletion(-)\n create mode 100644 git-rb-setup.rb\n create mode 100644 ruby.c\n create mode 100755 t/t10000-ruby.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex 6b1fd1b..51b04be 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -139,6 +139,7 @@\n /git-rev-parse\n /git-revert\n /git-rm\n+/git-ruby\n /git-send-email\n /git-send-pack\n /git-sh-i18n\ndiff --git a/Makefile b/Makefile\nindex 3588ca1..2fdee15 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -491,6 +491,8 @@ SCRIPT_PERL += git-svn.perl\n SCRIPT_PYTHON += git-remote-testpy.py\n SCRIPT_PYTHON += git-p4.py\n \n+SCRIPT_RUBY += git-rb-setup.rb\n+\n NO_INSTALL += git-remote-testgit\n NO_INSTALL += git-remote-testpy\n \n@@ -502,6 +504,7 @@ SCRIPT_PYTHON_GEN = $(patsubst %.py,%,$(SCRIPT_PYTHON))\n SCRIPT_SH_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_SH_GEN))\n SCRIPT_PERL_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_PERL_GEN))\n SCRIPT_PYTHON_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_PYTHON_GEN))\n+SCRIPT_RUBY_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_RUBY))\n \n # Individual rules to allow e.g.\n # \"make -C ../.. SCRIPT_PERL=contrib/foo/bar.perl build-perl-script\"\n@@ -511,13 +514,15 @@ build-perl-script: $(SCRIPT_PERL_GEN)\n build-sh-script: $(SCRIPT_SH_GEN)\n build-python-script: $(SCRIPT_PYTHON_GEN)\n \n-.PHONY: install-perl-script install-sh-script install-python-script\n+.PHONY: install-perl-script install-sh-script install-python-script install-ruby-script\n install-sh-script: $(SCRIPT_SH_INS)\n \t$(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'\n install-perl-script: $(SCRIPT_PERL_INS)\n \t$(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'\n install-python-script: $(SCRIPT_PYTHON_INS)\n \t$(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'\n+install-ruby-script: $(SCRIPT_RUBY_INS)\n+\t$(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'\n \n .PHONY: clean-perl-script clean-sh-script clean-python-script\n clean-sh-script:\n@@ -530,6 +535,7 @@ clean-python-script:\n SCRIPTS = $(SCRIPT_SH_INS) \\\n \t  $(SCRIPT_PERL_INS) \\\n \t  $(SCRIPT_PYTHON_INS) \\\n+\t  $(SCRIPT_RUBY_INS) \\\n \t  git-instaweb\n \n ETAGS_TARGET = TAGS\n@@ -1502,6 +1508,12 @@ ifneq (,$(XDL_FAST_HASH))\n \tBASIC_CFLAGS += -DXDL_FAST_HASH\n endif\n \n+ifndef NO_RUBY\n+\tRUBY_LIBS = $(shell pkg-config --libs ruby-2.0)\n+\tRUBY_CFLAGS = $(shell pkg-config --cflags ruby-2.0)\n+\tPROGRAM_OBJS += ruby.o\n+endif\n+\n ifeq ($(TCLTK_PATH),)\n NO_TCLTK = NoThanks\n endif\n@@ -2059,6 +2071,11 @@ git-http-push$X: revision.o http.o http-push.o GIT-LDFLAGS $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)\n \n+git-ruby$X: BASIC_CFLAGS += $(RUBY_CFLAGS)\n+git-ruby$X: ruby.o GIT-LDFLAGS $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(RUBY_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n+\t\t$(LIBS) $(RUBY_LIBS)\n+\n git-remote-testsvn$X: remote-testsvn.o GIT-LDFLAGS $(GITLIBS) $(VCSSVN_LIB)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) \\\n \t$(VCSSVN_LIB)\ndiff --git a/git-rb-setup.rb b/git-rb-setup.rb\nnew file mode 100644\nindex 0000000..969278a\n--- /dev/null\n+++ b/git-rb-setup.rb\n@@ -0,0 +1,11 @@\n+#!/usr/bin/env ruby\n+\n+def die(*args)\n+  fmt = args.shift\n+  $stderr.printf(\"fatal: %s\\n\" % fmt, *args)\n+  exit 128\n+end\n+\n+def sha1_to_hex(sha1)\n+  sha1.unpack('H*').first\n+end\ndiff --git a/ruby.c b/ruby.c\nnew file mode 100644\nindex 0000000..ee6a0e7\n--- /dev/null\n+++ b/ruby.c\n@@ -0,0 +1,56 @@\n+#include \"cache.h\"\n+#include \"exec_cmd.h\"\n+#include \"refs.h\"\n+\n+#undef NORETURN\n+#undef PATH_SEP\n+\n+#include <ruby.h>\n+\n+static inline VALUE sha1_to_str(const unsigned char *sha1)\n+{\n+\treturn rb_str_new((const char *)sha1, 20);\n+}\n+\n+static int for_each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data)\n+{\n+\tVALUE r;\n+\tr = rb_yield_values(3, rb_str_new2(refname), sha1_to_str(sha1), INT2FIX(flags));\n+\treturn r == Qfalse;\n+}\n+\n+static VALUE git_rb_for_each_ref(void)\n+{\n+\tint r;\n+\tr = for_each_ref(for_each_ref_fn, NULL);\n+\treturn INT2FIX(r);\n+}\n+\n+static void git_ruby_init(void)\n+{\n+\trb_define_global_function(\"for_each_ref\", git_rb_for_each_ref, 0);\n+}\n+\n+static int run_ruby_command(const char *cmd, int argc, const char **argv)\n+{\n+\tstatic char buf[PATH_MAX + 1];\n+\tvoid *node;\n+\tstruct stat st;\n+\n+\truby_init();\n+\tgit_ruby_init();\n+\n+\tnode = ruby_options(argc, (char **)argv);\n+\n+\truby_script(cmd);\n+\tsnprintf(buf, PATH_MAX, \"%s/%s\", git_exec_path(), \"git-rb-setup.rb\");\n+\tif (!stat(buf, &st))\n+\t\trb_load(rb_str_new2(buf), 0);\n+\n+\treturn ruby_run_node(node);\n+}\n+\n+int main(int argc, const char **argv)\n+{\n+\treturn run_ruby_command(argv[1], argc, argv);\n+}\ndiff --git a/t/t10000-ruby.sh b/t/t10000-ruby.sh\nnew file mode 100755\nindex 0000000..eb03706\n--- /dev/null\n+++ b/t/t10000-ruby.sh\n@@ -0,0 +1,42 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2013 Felipe Contreras\n+#\n+\n+test_description='test ruby support'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'basic support' '\n+\tgit ruby > actual <<-EOF &&\n+\tputs \"hello world\"\n+\tEOF\n+\techo \"hello world\" > expected &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'argument passing' '\n+\tcat > script <<-\"EOF\" &&\n+\tp($0)\n+\tp(ARGV)\n+\tEOF\n+\tgit ruby script foo bar > actual &&\n+\tcat > expected <<-EOF &&\n+\t\"script\"\n+\t[\"foo\", \"bar\"]\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'test for_each_ref()' '\n+\ttest_commit foo &&\n+\tgit ruby > actual <<-EOF &&\n+\tfor_each_ref() do |name, sha1, flags|\n+\t\tputs \"%s: %s\" % [name, sha1_to_hex(sha1)]\n+\tend\n+\tEOF\n+\tgit for-each-ref --format=\"%(refname): %(objectname)\" > expected &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n1.8.4-fc\n"},{"id":"228380","messageId":"1380405849-13000-3-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 02/44] ruby: add support for internal ruby programs","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:27Z","receivedAt":"2013-09-28T22:03:27Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n Makefile |  9 +++++++++\n ruby.c   | 19 ++++++++++++++++++-\n 2 files changed, 27 insertions(+), 1 deletion(-)\n\ndiff --git a/Makefile b/Makefile\nindex 2fdee15..27e61c0 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -429,6 +429,7 @@ BASIC_LDFLAGS =\n # Guard against environment variables\n BUILTIN_OBJS =\n BUILT_INS =\n+RUBY_PROGRAMS =\n COMPAT_CFLAGS =\n COMPAT_OBJS =\n XDIFF_OBJS =\n@@ -493,6 +494,8 @@ SCRIPT_PYTHON += git-p4.py\n \n SCRIPT_RUBY += git-rb-setup.rb\n \n+PROGRAMS += $(RUBY_PROGRAMS)\n+\n NO_INSTALL += git-remote-testgit\n NO_INSTALL += git-remote-testpy\n \n@@ -1754,6 +1757,12 @@ $(BUILT_INS): git$X\n \tln -s $< $@ 2>/dev/null || \\\n \tcp $< $@\n \n+$(RUBY_PROGRAMS): git-ruby$X\n+\t$(QUIET_BUILT_IN)$(RM) $@ && \\\n+\tln $< $@ 2>/dev/null || \\\n+\tln -s $< $@ 2>/dev/null || \\\n+\tcp $< $@\n+\n common-cmds.h: ./generate-cmdlist.sh command-list.txt\n \n common-cmds.h: $(wildcard Documentation/git-*.txt)\ndiff --git a/ruby.c b/ruby.c\nindex ee6a0e7..339e376 100644\n--- a/ruby.c\n+++ b/ruby.c\n@@ -52,5 +52,22 @@ static int run_ruby_command(const char *cmd, int argc, const char **argv)\n \n int main(int argc, const char **argv)\n {\n-\treturn run_ruby_command(argv[1], argc, argv);\n+\tif (!strcmp(argv[0], \"git-ruby\")) {\n+\t\treturn run_ruby_command(argv[1], argc, argv);\n+\t} else {\n+\t\tconst char *cmd = argv[0];\n+\t\tstatic char buf[PATH_MAX + 1];\n+\t\tconst char *args[argc + 1];\n+\t\tint i;\n+\n+\t\tsnprintf(buf, PATH_MAX, \"%s/%s.rb\",\n+\t\t\t\tgit_exec_path(), basename((char *)cmd));\n+\n+\t\targs[0] = \"git\";\n+\t\targs[1] = buf;\n+\t\tfor (i = 0; i < argc - 1; i++)\n+\t\t\targs[i + 2] = (char *)argv[i + 1];\n+\n+\t\treturn run_ruby_command(cmd, argc + 1, args);\n+\t}\n }\n-- \n1.8.4-fc\n"},{"id":"228379","messageId":"1380405849-13000-4-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 03/44] request-pull: fix annotated tag check","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:28Z","receivedAt":"2013-09-28T22:03:28Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"The was just not doing anything, reporting an appropriately tagged ref\nworked only by luck.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n git-request-pull.sh     |  2 +-\n t/t5150-request-pull.sh | 15 +++++++++++++++\n 2 files changed, 16 insertions(+), 1 deletion(-)\n\ndiff --git a/git-request-pull.sh b/git-request-pull.sh\nindex ebf1269..6348dac 100755\n--- a/git-request-pull.sh\n+++ b/git-request-pull.sh\n@@ -89,7 +89,7 @@ find_matching_ref='\n \t\tmy ($sha1, $ref, $deref) = /^(\\S+)\\s+(\\S+?)(\\^\\{\\})?$/;\n \t\tnext unless ($sha1 eq $ARGV[1]);\n \t\t$found = abbr($ref);\n-\t\tif ($deref && $ref eq \"tags/$ARGV[2]\") {\n+\t\tif ($deref && $ref eq \"refs/tags/$ARGV[2]\") {\n \t\t\t$tagged = $found;\n \t\t\tlast;\n \t\t}\ndiff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh\nindex 1afa0d5..54f41bf 100755\n--- a/t/t5150-request-pull.sh\n+++ b/t/t5150-request-pull.sh\n@@ -234,4 +234,19 @@ test_expect_success 'request-pull ignores OPTIONS_KEEPDASHDASH poison' '\n \n '\n \n+test_expect_success 'pull request when pushed tag' '\n+\trm -fr downstream.git &&\n+\tgit init --bare downstream.git &&\n+\t(\n+\t\tcd local &&\n+\t\tgit checkout initial &&\n+\t\tgit merge --ff-only master &&\n+\t\tgit tag zeebra &&\n+\t\tgit push origin master:for-upstream full zeebra &&\n+\t\tgit request-pull initial origin 2>../err\n+\t) &&\n+\tcat err &&\n+\t! grep \"You locally have .* but it does not (yet)\" err\n+'\n+\n test_done\n-- \n1.8.4-fc\n"},{"id":"228381","messageId":"1380405849-13000-5-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 04/44] request-pull: fix for specific branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:29Z","receivedAt":"2013-09-28T22:03:29Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Wether we use a symbolic ref, or we specify the branch directly\nshouldn't really matter, we would want the branch description either\nway.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n git-request-pull.sh     | 13 +++++--------\n t/t5150-request-pull.sh | 32 ++++++++++++++++++++++++++++++++\n 2 files changed, 37 insertions(+), 8 deletions(-)\n\ndiff --git a/git-request-pull.sh b/git-request-pull.sh\nindex 6348dac..3c9a982 100755\n--- a/git-request-pull.sh\n+++ b/git-request-pull.sh\n@@ -37,15 +37,12 @@ done\n \n base=$1 url=$2 head=${3-HEAD} status=0 branch_name=\n \n-headref=$(git symbolic-ref -q \"$head\")\n-if git show-ref -q --verify \"$headref\"\n+headref=$(git rev-parse -q --verify --symbolic-full-name \"$head\")\n+branch_name=${headref#refs/heads/}\n+if test \"z$branch_name\" = \"z$headref\" ||\n+\t! git config \"branch.$branch_name.description\" >/dev/null\n then\n-\tbranch_name=${headref#refs/heads/}\n-\tif test \"z$branch_name\" = \"z$headref\" ||\n-\t\t! git config \"branch.$branch_name.description\" >/dev/null\n-\tthen\n-\t\tbranch_name=\n-\tfi\n+\tbranch_name=\n fi\n \n tag_name=$(git describe --exact \"$head^0\" 2>/dev/null)\ndiff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh\nindex 54f41bf..a9ee96a 100755\n--- a/t/t5150-request-pull.sh\n+++ b/t/t5150-request-pull.sh\n@@ -249,4 +249,36 @@ test_expect_success 'pull request when pushed tag' '\n \t! grep \"You locally have .* but it does not (yet)\" err\n '\n \n+test_expect_success 'pull request with branch description' '\n+\ttest_when_finished \"(cd local && git checkout - && git branch -D for-upstream)\" &&\n+\trm -fr downstream.git &&\n+\tgit init --bare downstream.git &&\n+\t(\n+\t\tcd local &&\n+\t\tgit checkout -b for-upstream master &&\n+\t\tgit config branch.for-upstream.description \"Branch for upstream$LF\" &&\n+\t\tgit push origin for-upstream &&\n+\t\tgit request-pull initial origin >../request\n+\t) &&\n+\tcat request &&\n+\tgrep \"(from the branch description for for-upstream local branch)\" request &&\n+\tgrep \"Branch for upstream\" request\n+'\n+\n+test_expect_success 'pull request with branch description from rev' '\n+\ttest_when_finished \"(cd local && git checkout - && git branch -D for-upstream)\" &&\n+\trm -fr downstream.git &&\n+\tgit init --bare downstream.git &&\n+\t(\n+\t\tcd local &&\n+\t\tgit checkout -b for-upstream master &&\n+\t\tgit config branch.for-upstream.description \"Branch for upstream$LF\" &&\n+\t\tgit push origin for-upstream &&\n+\t\tgit request-pull initial origin for-upstream >../request\n+\t) &&\n+\tcat request &&\n+\tgrep \"(from the branch description for for-upstream local branch)\" request &&\n+\tgrep \"Branch for upstream\" request\n+'\n+\n test_done\n-- \n1.8.4-fc\n"},{"id":"228382","messageId":"1380405849-13000-6-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 05/44] request-pull: use appropriate ref","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:30Z","receivedAt":"2013-09-28T22:03:30Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"\"Are you sure you pushed 'HEAD'?\" doesn't say anything helpful.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n git-request-pull.sh | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/git-request-pull.sh b/git-request-pull.sh\nindex 3c9a982..3e510ca 100755\n--- a/git-request-pull.sh\n+++ b/git-request-pull.sh\n@@ -151,9 +151,10 @@ git diff -M --stat --summary $patch $merge_base..$headrev || status=1\n \n if test -z \"$ref\"\n then\n+\tshort_headref=$(git rev-parse -q --verify --symbolic-full-name --abbrev-ref \"$head\")\n \techo \"warn: No branch of $url is at:\" >&2\n \tgit show -s --format='warn:   %h: %s' $headrev >&2\n-\techo \"warn: Are you sure you pushed '$head' there?\" >&2\n+\techo \"warn: Are you sure you pushed '$short_headref' there?\" >&2\n \tstatus=1\n fi\n exit $status\n-- \n1.8.4-fc\n"},{"id":"228383","messageId":"1380405849-13000-7-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 06/44] request-pull: fix exact match checking","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:31Z","receivedAt":"2013-09-28T22:03:31Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"It obviously never worked, refs don't start with '/'.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n git-request-pull.sh     | 10 ++++++++--\n t/t5150-request-pull.sh | 21 +++++++++++++++++++++\n 2 files changed, 29 insertions(+), 2 deletions(-)\n\ndiff --git a/git-request-pull.sh b/git-request-pull.sh\nindex 3e510ca..c970244 100755\n--- a/git-request-pull.sh\n+++ b/git-request-pull.sh\n@@ -38,6 +38,11 @@ done\n base=$1 url=$2 head=${3-HEAD} status=0 branch_name=\n \n headref=$(git rev-parse -q --verify --symbolic-full-name \"$head\")\n+if test \"$headref\" = \"HEAD\"\n+then\n+\theadref=\n+fi\n+\n branch_name=${headref#refs/heads/}\n if test \"z$branch_name\" = \"z$headref\" ||\n \t! git config \"branch.$branch_name.description\" >/dev/null\n@@ -90,8 +95,9 @@ find_matching_ref='\n \t\t\t$tagged = $found;\n \t\t\tlast;\n \t\t}\n-\t\tif ($ref =~ m|/\\Q$ARGV[0]\\E$|) {\n+\t\tif ($ref eq $ARGV[0]) {\n \t\t\t$exact = $found;\n+\t\t\tlast;\n \t\t}\n \t}\n \tif ($tagged) {\n@@ -103,7 +109,7 @@ find_matching_ref='\n \t}\n '\n \n-ref=$(git ls-remote \"$url\" | perl -e \"$find_matching_ref\" \"$head\" \"$headrev\" \"$tag_name\")\n+ref=$(git ls-remote \"$url\" | perl -e \"$find_matching_ref\" \"$headref\" \"$headrev\" \"$tag_name\")\n \n url=$(git ls-remote --get-url \"$url\")\n \ndiff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh\nindex a9ee96a..73f0369 100755\n--- a/t/t5150-request-pull.sh\n+++ b/t/t5150-request-pull.sh\n@@ -281,4 +281,25 @@ test_expect_success 'pull request with branch description from rev' '\n \tgrep \"Branch for upstream\" request\n '\n \n+test_expect_success 'pull request with exact match' '\n+\ttest_when_finished \"(cd local && git checkout - && git branch -D for-upstream)\" &&\n+\trm -fr downstream.git &&\n+\tgit init --bare downstream.git &&\n+\t(\n+\t\tcd local &&\n+\t\tgit checkout -b for-upstream master &&\n+\t\tgit push origin master:for-upstream master:zeebra &&\n+\t\tgit request-pull initial origin for-upstream >../request\n+\t) &&\n+\tcat request &&\n+\tsed -nf read-request.sed <request >digest &&\n+\tcat digest &&\n+\t{\n+\t\tread task &&\n+\t\tread repository &&\n+\t\tread branch\n+\t} <digest &&\n+\ttest \"$branch\" = for-upstream\n+'\n+\n test_done\n-- \n1.8.4-fc\n"},{"id":"228384","messageId":"1380405849-13000-8-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 07/44] request-pull: trivial simplification","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:32Z","receivedAt":"2013-09-28T22:03:32Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n git-request-pull.sh | 10 ++--------\n 1 file changed, 2 insertions(+), 8 deletions(-)\n\ndiff --git a/git-request-pull.sh b/git-request-pull.sh\nindex c970244..d8b4c3c 100755\n--- a/git-request-pull.sh\n+++ b/git-request-pull.sh\n@@ -86,25 +86,19 @@ find_matching_ref='\n \t\t}\n \t}\n \n-\tmy ($tagged, $branch, $found);\n+\tmy ($found);\n \twhile (<STDIN>) {\n \t\tmy ($sha1, $ref, $deref) = /^(\\S+)\\s+(\\S+?)(\\^\\{\\})?$/;\n \t\tnext unless ($sha1 eq $ARGV[1]);\n \t\t$found = abbr($ref);\n \t\tif ($deref && $ref eq \"refs/tags/$ARGV[2]\") {\n-\t\t\t$tagged = $found;\n \t\t\tlast;\n \t\t}\n \t\tif ($ref eq $ARGV[0]) {\n-\t\t\t$exact = $found;\n \t\t\tlast;\n \t\t}\n \t}\n-\tif ($tagged) {\n-\t\tprint \"$tagged\\n\";\n-\t} elsif ($exact) {\n-\t\tprint \"$exact\\n\";\n-\t} elsif ($found) {\n+\tif ($found) {\n \t\tprint \"$found\\n\";\n \t}\n '\n-- \n1.8.4-fc\n"},{"id":"228386","messageId":"1380405849-13000-9-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 08/44] request-pull: t: trivial whitespace style fixes","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:33Z","receivedAt":"2013-09-28T22:03:33Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n t/t5150-request-pull.sh | 14 --------------\n 1 file changed, 14 deletions(-)\n\ndiff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh\nindex 73f0369..9375912 100755\n--- a/t/t5150-request-pull.sh\n+++ b/t/t5150-request-pull.sh\n@@ -5,7 +5,6 @@ test_description='Test workflows involving pull request.'\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n-\n \tgit init --bare upstream.git &&\n \tgit init --bare downstream.git &&\n \tgit clone upstream.git upstream-private &&\n@@ -55,11 +54,9 @@ test_expect_success 'setup' '\n But keep the old version, too, in case some people prefer it.\" &&\n \t\tgit checkout master\n \t)\n-\n '\n \n test_expect_success 'setup: two scripts for reading pull requests' '\n-\n \tdownstream_url_for_sed=$(\n \t\tprintf \"%s\\n\" \"$downstream_url\" |\n \t\tsed -e '\\''s/\\\\/\\\\\\\\/g'\\'' -e '\\''s/[[/.*^$]/\\\\&/g'\\''\n@@ -113,11 +110,9 @@ test_expect_success 'setup: two scripts for reading pull requests' '\n \tn\n \tb shortlog\n \tEOT\n-\n '\n \n test_expect_success 'pull request when forgot to push' '\n-\n \trm -fr downstream.git &&\n \tgit init --bare downstream.git &&\n \t(\n@@ -129,11 +124,9 @@ test_expect_success 'pull request when forgot to push' '\n \t) &&\n \tgrep \"No branch of.*is at:\\$\" err &&\n \tgrep \"Are you sure you pushed\" err\n-\n '\n \n test_expect_success 'pull request after push' '\n-\n \trm -fr downstream.git &&\n \tgit init --bare downstream.git &&\n \t(\n@@ -157,11 +150,9 @@ test_expect_success 'pull request after push' '\n \t) &&\n \ttest \"$branch\" = for-upstream &&\n \ttest_cmp local/mnemonic.txt upstream-private/mnemonic.txt\n-\n '\n \n test_expect_success 'request names an appropriate branch' '\n-\n \trm -fr downstream.git &&\n \tgit init --bare downstream.git &&\n \t(\n@@ -180,11 +171,9 @@ test_expect_success 'request names an appropriate branch' '\n \t\tread branch\n \t} <digest &&\n \ttest \"$branch\" = tags/full\n-\n '\n \n test_expect_success 'pull request format' '\n-\n \trm -fr downstream.git &&\n \tgit init --bare downstream.git &&\n \tcat <<-\\EOT >expect &&\n@@ -217,11 +206,9 @@ test_expect_success 'pull request format' '\n \t) &&\n \t<request sed -nf fuzz.sed >request.fuzzy &&\n \ttest_i18ncmp expect request.fuzzy\n-\n '\n \n test_expect_success 'request-pull ignores OPTIONS_KEEPDASHDASH poison' '\n-\n \t(\n \t\tcd local &&\n \t\tOPTIONS_KEEPDASHDASH=Yes &&\n@@ -231,7 +218,6 @@ test_expect_success 'request-pull ignores OPTIONS_KEEPDASHDASH poison' '\n \t\tgit push origin master:for-upstream &&\n \t\tgit request-pull -- initial \"$downstream_url\" >../request\n \t)\n-\n '\n \n test_expect_success 'pull request when pushed tag' '\n-- \n1.8.4-fc\n"},{"id":"228387","messageId":"1380405849-13000-10-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 09/44] request-pull: t: add missing cat","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:34Z","receivedAt":"2013-09-28T22:03:34Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Otherwise we wouldn't know why it failed.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n t/t5150-request-pull.sh | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh\nindex 9375912..60a38be 100755\n--- a/t/t5150-request-pull.sh\n+++ b/t/t5150-request-pull.sh\n@@ -122,6 +122,7 @@ test_expect_success 'pull request when forgot to push' '\n \t\ttest_must_fail git request-pull initial \"$downstream_url\" \\\n \t\t\t2>../err\n \t) &&\n+\tcat err &&\n \tgrep \"No branch of.*is at:\\$\" err &&\n \tgrep \"Are you sure you pushed\" err\n '\n-- \n1.8.4-fc\n"},{"id":"228385","messageId":"1380405849-13000-11-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 10/44] ruby: rewrite 'request-pull'","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:35Z","receivedAt":"2013-09-28T22:03:35Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    The objective is to be as close to the original as possible, to minimize\n    regressions, even if the style is not very Ruby-ish.\n\n Makefile            |   3 +-\n git-rb-setup.rb     |  17 ++++++\n git-request-pull.rb | 146 +++++++++++++++++++++++++++++++++++++++++++++++\n git-request-pull.sh | 160 ----------------------------------------------------\n 4 files changed, 165 insertions(+), 161 deletions(-)\n create mode 100755 git-request-pull.rb\n delete mode 100755 git-request-pull.sh\n\ndiff --git a/Makefile b/Makefile\nindex 27e61c0..44b4cd5 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -466,7 +466,6 @@ SCRIPT_SH += git-quiltimport.sh\n SCRIPT_SH += git-rebase.sh\n SCRIPT_SH += git-remote-testgit.sh\n SCRIPT_SH += git-repack.sh\n-SCRIPT_SH += git-request-pull.sh\n SCRIPT_SH += git-stash.sh\n SCRIPT_SH += git-submodule.sh\n SCRIPT_SH += git-web--browse.sh\n@@ -493,7 +492,9 @@ SCRIPT_PYTHON += git-remote-testpy.py\n SCRIPT_PYTHON += git-p4.py\n \n SCRIPT_RUBY += git-rb-setup.rb\n+SCRIPT_RUBY += git-request-pull.rb\n \n+RUBY_PROGRAMS += git-request-pull$X\n PROGRAMS += $(RUBY_PROGRAMS)\n \n NO_INSTALL += git-remote-testgit\ndiff --git a/git-rb-setup.rb b/git-rb-setup.rb\nindex 969278a..92998a2 100644\n--- a/git-rb-setup.rb\n+++ b/git-rb-setup.rb\n@@ -9,3 +9,20 @@ end\n def sha1_to_hex(sha1)\n   sha1.unpack('H*').first\n end\n+\n+class CommandError < RuntimeError\n+\n+  def initialize(command)\n+     @command = command\n+  end\n+\n+  def to_s\n+    Array(@command).join(' ').inspect\n+  end\n+\n+end\n+\n+def run(cmd, *args)\n+  system(*cmd, *args)\n+  raise CommandError.new(cmd) unless $?.success?\n+end\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nnew file mode 100755\nindex 0000000..60cf6ce\n--- /dev/null\n+++ b/git-request-pull.rb\n@@ -0,0 +1,146 @@\n+#!git ruby\n+\n+ENV['GIT_PAGER'] =\n+\n+patch = ''\n+\n+def usage\n+  puts <<EOF\n+usage: git request-pull [options] start url [end]\n+\n+    -p                    show patch text as well\n+\n+EOF\n+  exit 1\n+end\n+\n+until ARGV.empty?\n+  case ARGV.first\n+  when '-p'\n+    patch = '-p'\n+  when '--'\n+    ARGV.shift\n+    break\n+  when /^-/\n+    usage\n+  else\n+    break\n+  end\n+  ARGV.shift\n+end\n+\n+base = ARGV[0]\n+url = ARGV[1]\n+head = ARGV[2] || 'HEAD'\n+status = 0\n+branch_name = nil\n+\n+headref = `git rev-parse -q --verify --symbolic-full-name \"#{head}\"`.chomp\n+headref = \"\" if headref == \"HEAD\"\n+\n+branch_name = headref.gsub(%r{^refs/heads/}, '')\n+if branch_name == headref ||\n+  ! system(%[git config \"branch.#{branch_name}.description\" >/dev/null])\n+  branch_name = nil\n+end\n+\n+tag_name = `git describe --exact \"#{head}^0\" 2>/dev/null`.chomp\n+\n+usage unless base or url\n+\n+baserev = `git rev-parse --verify --quiet \"#{base}\"^0`.chomp\n+die \"Not a valid revision: #{base}\" if baserev.empty?\n+\n+headrev = `git rev-parse --verify --quiet \"#{head}\"^0`.chomp\n+die \"Not a valid revision: #{head}\" if headrev.empty?\n+\n+merge_base = `git merge-base #{baserev} #{headrev}`.chomp\n+die \"No commits in common between #{base} and #{head}\" unless $?.success?\n+\n+# $head is the token given from the command line, and $tag_name, if\n+# exists, is the tag we are going to show the commit information for.\n+# If that tag exists at the remote and it points at the commit, use it.\n+# Otherwise, if a branch with the same name as $head exists at the remote\n+# and their values match, use that instead.\n+#\n+# Otherwise find a random ref that matches $headrev.\n+find_matching_ref='\n+\tsub abbr {\n+\t\tmy $ref = shift;\n+\t\tif ($ref =~ s|^refs/heads/|| || $ref =~ s|^refs/tags/|tags/|) {\n+\t\t\treturn $ref;\n+\t\t} else {\n+\t\t\treturn $ref;\n+\t\t}\n+\t}\n+\n+\tmy ($found);\n+\twhile (<STDIN>) {\n+\t\tmy ($sha1, $ref, $deref) = /^(\\S+)\\s+(\\S+?)(\\^\\{\\})?$/;\n+\t\tnext unless ($sha1 eq $ARGV[1]);\n+\t\t$found = abbr($ref);\n+\t\tif ($deref && $ref eq \"refs/tags/$ARGV[2]\") {\n+\t\t\tlast;\n+\t\t}\n+\t\tif ($ref eq $ARGV[0]) {\n+\t\t\tlast;\n+\t\t}\n+\t}\n+\tif ($found) {\n+\t\tprint \"$found\\n\";\n+\t}\n+'\n+\n+ref = `git ls-remote \"#{url}\" | perl -e '#{find_matching_ref}' \"#{headref}\" \"#{headrev}\" \"#{tag_name}\"`.chomp\n+url = `git ls-remote --get-url \"#{url}\"`.chomp\n+\n+begin\n+  run(%[git show -s --format='The following changes since commit %H:\n+\n+  %s (%ci)\n+\n+are available in the git repository at:\n+' #{merge_base}])\n+  puts \"  #{url}\" + (ref.empty? ? \"\" : \" #{ref}\")\n+  run(%[git show -s --format='\n+for you to fetch changes up to %H:\n+\n+  %s (%ci)\n+\n+----------------------------------------------------------------' #{headrev}])\n+\n+  if branch_name\n+    puts \"(from the branch description for #{branch_name} local branch)\"\n+    puts\n+    run(%[git config \"branch.#{branch_name}.description\"])\n+  end\n+\n+  if not tag_name.empty?\n+    if ref.empty? || ref != \"tags/#{tag_name}\"\n+      $stderr.puts \"warn: You locally have #{tag_name} but it does not (yet)\"\n+      $stderr.puts \"warn: appear to be at #{url}\"\n+      $stderr.puts \"warn: Do you want to push it there, perhaps?\"\n+    end\n+    run(%[git cat-file tag \"#{tag_name}\" | sed -n -e '1,/^$/d' -e '/^-----BEGIN PGP /q' -e p])\n+    puts\n+  end\n+\n+  if branch_name || ! tag_name.empty?\n+    puts \"----------------------------------------------------------------\"\n+  end\n+\n+  run(%[git shortlog ^#{baserev} #{headrev}])\n+  run(%[git diff -M --stat --summary #{patch} #{merge_base}..#{headrev}])\n+\n+  if ref.empty?\n+    short_headref = `git rev-parse -q --verify --symbolic-full-name --abbrev-ref \"#{head}\"`.chomp\n+    $stderr.puts \"warn: No branch of #{url} is at:\"\n+    run(\"git show -s --format='warn:   %h: %s' #{headrev} >&2\")\n+    $stderr.puts \"warn: Are you sure you pushed '#{short_headref}' there?\"\n+    status = 1\n+  end\n+rescue CommandError\n+  status = 1\n+end\n+\n+exit status\ndiff --git a/git-request-pull.sh b/git-request-pull.sh\ndeleted file mode 100755\nindex d8b4c3c..0000000\n--- a/git-request-pull.sh\n+++ /dev/null\n@@ -1,160 +0,0 @@\n-#!/bin/sh\n-# Copyright 2005, Ryan Anderson <ryan@michonline.com>\n-#\n-# This file is licensed under the GPL v2, or a later version\n-# at the discretion of Linus Torvalds.\n-\n-USAGE='<start> <url> [<end>]'\n-LONG_USAGE='Summarizes the changes between two commits to the standard output,\n-and includes the given URL in the generated summary.'\n-SUBDIRECTORY_OK='Yes'\n-OPTIONS_KEEPDASHDASH=\n-OPTIONS_SPEC='git request-pull [options] start url [end]\n---\n-p    show patch text as well\n-'\n-\n-. git-sh-setup\n-\n-GIT_PAGER=\n-export GIT_PAGER\n-\n-patch=\n-while\tcase \"$#\" in 0) break ;; esac\n-do\n-\tcase \"$1\" in\n-\t-p)\n-\t\tpatch=-p ;;\n-\t--)\n-\t\tshift; break ;;\n-\t-*)\n-\t\tusage ;;\n-\t*)\n-\t\tbreak ;;\n-\tesac\n-\tshift\n-done\n-\n-base=$1 url=$2 head=${3-HEAD} status=0 branch_name=\n-\n-headref=$(git rev-parse -q --verify --symbolic-full-name \"$head\")\n-if test \"$headref\" = \"HEAD\"\n-then\n-\theadref=\n-fi\n-\n-branch_name=${headref#refs/heads/}\n-if test \"z$branch_name\" = \"z$headref\" ||\n-\t! git config \"branch.$branch_name.description\" >/dev/null\n-then\n-\tbranch_name=\n-fi\n-\n-tag_name=$(git describe --exact \"$head^0\" 2>/dev/null)\n-\n-test -n \"$base\" && test -n \"$url\" || usage\n-\n-baserev=$(git rev-parse --verify --quiet \"$base\"^0)\n-if test -z \"$baserev\"\n-then\n-    die \"fatal: Not a valid revision: $base\"\n-fi\n-\n-headrev=$(git rev-parse --verify --quiet \"$head\"^0)\n-if test -z \"$headrev\"\n-then\n-    die \"fatal: Not a valid revision: $head\"\n-fi\n-\n-merge_base=$(git merge-base $baserev $headrev) ||\n-die \"fatal: No commits in common between $base and $head\"\n-\n-# $head is the token given from the command line, and $tag_name, if\n-# exists, is the tag we are going to show the commit information for.\n-# If that tag exists at the remote and it points at the commit, use it.\n-# Otherwise, if a branch with the same name as $head exists at the remote\n-# and their values match, use that instead.\n-#\n-# Otherwise find a random ref that matches $headrev.\n-find_matching_ref='\n-\tsub abbr {\n-\t\tmy $ref = shift;\n-\t\tif ($ref =~ s|^refs/heads/|| || $ref =~ s|^refs/tags/|tags/|) {\n-\t\t\treturn $ref;\n-\t\t} else {\n-\t\t\treturn $ref;\n-\t\t}\n-\t}\n-\n-\tmy ($found);\n-\twhile (<STDIN>) {\n-\t\tmy ($sha1, $ref, $deref) = /^(\\S+)\\s+(\\S+?)(\\^\\{\\})?$/;\n-\t\tnext unless ($sha1 eq $ARGV[1]);\n-\t\t$found = abbr($ref);\n-\t\tif ($deref && $ref eq \"refs/tags/$ARGV[2]\") {\n-\t\t\tlast;\n-\t\t}\n-\t\tif ($ref eq $ARGV[0]) {\n-\t\t\tlast;\n-\t\t}\n-\t}\n-\tif ($found) {\n-\t\tprint \"$found\\n\";\n-\t}\n-'\n-\n-ref=$(git ls-remote \"$url\" | perl -e \"$find_matching_ref\" \"$headref\" \"$headrev\" \"$tag_name\")\n-\n-url=$(git ls-remote --get-url \"$url\")\n-\n-git show -s --format='The following changes since commit %H:\n-\n-  %s (%ci)\n-\n-are available in the git repository at:\n-' $merge_base &&\n-echo \"  $url${ref+ $ref}\" &&\n-git show -s --format='\n-for you to fetch changes up to %H:\n-\n-  %s (%ci)\n-\n-----------------------------------------------------------------' $headrev &&\n-\n-if test -n \"$branch_name\"\n-then\n-\techo \"(from the branch description for $branch_name local branch)\"\n-\techo\n-\tgit config \"branch.$branch_name.description\"\n-fi &&\n-\n-if test -n \"$tag_name\"\n-then\n-\tif test -z \"$ref\" || test \"$ref\" != \"tags/$tag_name\"\n-\tthen\n-\t\techo >&2 \"warn: You locally have $tag_name but it does not (yet)\"\n-\t\techo >&2 \"warn: appear to be at $url\"\n-\t\techo >&2 \"warn: Do you want to push it there, perhaps?\"\n-\tfi\n-\tgit cat-file tag \"$tag_name\" |\n-\tsed -n -e '1,/^$/d' -e '/^-----BEGIN PGP /q' -e p\n-\techo\n-fi &&\n-\n-if test -n \"$branch_name\" || test -n \"$tag_name\"\n-then\n-\techo \"----------------------------------------------------------------\"\n-fi &&\n-\n-git shortlog ^$baserev $headrev &&\n-git diff -M --stat --summary $patch $merge_base..$headrev || status=1\n-\n-if test -z \"$ref\"\n-then\n-\tshort_headref=$(git rev-parse -q --verify --symbolic-full-name --abbrev-ref \"$head\")\n-\techo \"warn: No branch of $url is at:\" >&2\n-\tgit show -s --format='warn:   %h: %s' $headrev >&2\n-\techo \"warn: Are you sure you pushed '$short_headref' there?\" >&2\n-\tstatus=1\n-fi\n-exit $status\n-- \n1.8.4-fc\n"},{"id":"228388","messageId":"1380405849-13000-12-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 11/44] ruby: request-pull: rewrite perl script","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:36Z","receivedAt":"2013-09-28T22:03:36Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Ruby can do it just fine, no need for perl.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    I'm mostly ignoring the comment, my hope is that it would be redundant, as the\n    code would be self-documenting.\n\n git-request-pull.rb | 65 +++++++++++++++++++++++++----------------------------\n 1 file changed, 30 insertions(+), 35 deletions(-)\n\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nindex 60cf6ce..eee2e28 100755\n--- a/git-request-pull.rb\n+++ b/git-request-pull.rb\n@@ -14,6 +14,35 @@ EOF\n   exit 1\n end\n \n+def abbr(ref)\n+  if (ref =~ %r{^refs/heads/(.*)} || ref =~ %r{^refs/(tags/.*)})\n+    return $1\n+  end\n+  return ref\n+end\n+\n+# $head is the token given from the command line, and $tag_name, if\n+# exists, is the tag we are going to show the commit information for.\n+# If that tag exists at the remote and it points at the commit, use it.\n+# Otherwise, if a branch with the same name as $head exists at the remote\n+# and their values match, use that instead.\n+#\n+# Otherwise find a random ref that matches $headrev.\n+\n+def get_ref(url, headref, headrev, tag_name)\n+  found = nil\n+  IO.popen(%[git ls-remote \"#{url}\"]) do |out|\n+    out.each do |l|\n+      sha1, ref, deref = l.scan(/^(\\S+)\\s+(\\S+?)(\\^\\{\\})?$/).first\n+      next unless sha1 == headrev\n+      found = abbr(ref)\n+      break if (deref && ref == \"refs/tags/#{tag_name}\")\n+      break if ref == headref\n+    end\n+  end\n+  return found\n+end\n+\n until ARGV.empty?\n   case ARGV.first\n   when '-p'\n@@ -57,41 +86,7 @@ die \"Not a valid revision: #{head}\" if headrev.empty?\n merge_base = `git merge-base #{baserev} #{headrev}`.chomp\n die \"No commits in common between #{base} and #{head}\" unless $?.success?\n \n-# $head is the token given from the command line, and $tag_name, if\n-# exists, is the tag we are going to show the commit information for.\n-# If that tag exists at the remote and it points at the commit, use it.\n-# Otherwise, if a branch with the same name as $head exists at the remote\n-# and their values match, use that instead.\n-#\n-# Otherwise find a random ref that matches $headrev.\n-find_matching_ref='\n-\tsub abbr {\n-\t\tmy $ref = shift;\n-\t\tif ($ref =~ s|^refs/heads/|| || $ref =~ s|^refs/tags/|tags/|) {\n-\t\t\treturn $ref;\n-\t\t} else {\n-\t\t\treturn $ref;\n-\t\t}\n-\t}\n-\n-\tmy ($found);\n-\twhile (<STDIN>) {\n-\t\tmy ($sha1, $ref, $deref) = /^(\\S+)\\s+(\\S+?)(\\^\\{\\})?$/;\n-\t\tnext unless ($sha1 eq $ARGV[1]);\n-\t\t$found = abbr($ref);\n-\t\tif ($deref && $ref eq \"refs/tags/$ARGV[2]\") {\n-\t\t\tlast;\n-\t\t}\n-\t\tif ($ref eq $ARGV[0]) {\n-\t\t\tlast;\n-\t\t}\n-\t}\n-\tif ($found) {\n-\t\tprint \"$found\\n\";\n-\t}\n-'\n-\n-ref = `git ls-remote \"#{url}\" | perl -e '#{find_matching_ref}' \"#{headref}\" \"#{headrev}\" \"#{tag_name}\"`.chomp\n+ref = get_ref(url, headref, headrev, tag_name) || ''\n url = `git ls-remote --get-url \"#{url}\"`.chomp\n \n begin\n-- \n1.8.4-fc\n"},{"id":"228389","messageId":"1380405849-13000-13-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 12/44] ruby: request-pull: trivial simplifications","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:37Z","receivedAt":"2013-09-28T22:03:37Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    Let's start deviating from the original script.\n    \n    There are no functional changes though.\n\n git-request-pull.rb | 34 ++++++++++++++++------------------\n 1 file changed, 16 insertions(+), 18 deletions(-)\n\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nindex eee2e28..9d3e9ab 100755\n--- a/git-request-pull.rb\n+++ b/git-request-pull.rb\n@@ -62,21 +62,20 @@ base = ARGV[0]\n url = ARGV[1]\n head = ARGV[2] || 'HEAD'\n status = 0\n-branch_name = nil\n+branch_name = branch_desc = nil\n+\n+usage unless base or url\n \n headref = `git rev-parse -q --verify --symbolic-full-name \"#{head}\"`.chomp\n-headref = \"\" if headref == \"HEAD\"\n \n-branch_name = headref.gsub(%r{^refs/heads/}, '')\n-if branch_name == headref ||\n-  ! system(%[git config \"branch.#{branch_name}.description\" >/dev/null])\n-  branch_name = nil\n+if headref.start_with?('refs/heads')\n+  branch_name = headref[11..-1]\n+  branch_desc = `git config \"branch.#{branch_name}.description\"`.chomp\n+  branch_name = nil if branch_desc.empty?\n end\n \n tag_name = `git describe --exact \"#{head}^0\" 2>/dev/null`.chomp\n \n-usage unless base or url\n-\n baserev = `git rev-parse --verify --quiet \"#{base}\"^0`.chomp\n die \"Not a valid revision: #{base}\" if baserev.empty?\n \n@@ -86,7 +85,7 @@ die \"Not a valid revision: #{head}\" if headrev.empty?\n merge_base = `git merge-base #{baserev} #{headrev}`.chomp\n die \"No commits in common between #{base} and #{head}\" unless $?.success?\n \n-ref = get_ref(url, headref, headrev, tag_name) || ''\n+ref = get_ref(url, headref != \"HEAD\" ? headref : nil, headrev, tag_name)\n url = `git ls-remote --get-url \"#{url}\"`.chomp\n \n begin\n@@ -96,7 +95,7 @@ begin\n \n are available in the git repository at:\n ' #{merge_base}])\n-  puts \"  #{url}\" + (ref.empty? ? \"\" : \" #{ref}\")\n+  puts \"  #{url}\" + (ref ? \" #{ref}\" : \"\")\n   run(%[git show -s --format='\n for you to fetch changes up to %H:\n \n@@ -107,11 +106,11 @@ for you to fetch changes up to %H:\n   if branch_name\n     puts \"(from the branch description for #{branch_name} local branch)\"\n     puts\n-    run(%[git config \"branch.#{branch_name}.description\"])\n+    puts branch_desc\n   end\n \n   if not tag_name.empty?\n-    if ref.empty? || ref != \"tags/#{tag_name}\"\n+    if ref != \"tags/#{tag_name}\"\n       $stderr.puts \"warn: You locally have #{tag_name} but it does not (yet)\"\n       $stderr.puts \"warn: appear to be at #{url}\"\n       $stderr.puts \"warn: Do you want to push it there, perhaps?\"\n@@ -124,14 +123,13 @@ for you to fetch changes up to %H:\n     puts \"----------------------------------------------------------------\"\n   end\n \n-  run(%[git shortlog ^#{baserev} #{headrev}])\n-  run(%[git diff -M --stat --summary #{patch} #{merge_base}..#{headrev}])\n+  run(%[git shortlog ^#{base} #{head}])\n+  run(%[git diff -M --stat --summary #{patch} ^#{merge_base} #{head}])\n \n-  if ref.empty?\n-    short_headref = `git rev-parse -q --verify --symbolic-full-name --abbrev-ref \"#{head}\"`.chomp\n+  if ! ref\n     $stderr.puts \"warn: No branch of #{url} is at:\"\n-    run(\"git show -s --format='warn:   %h: %s' #{headrev} >&2\")\n-    $stderr.puts \"warn: Are you sure you pushed '#{short_headref}' there?\"\n+    run(\"git show -s --format='warn:   %h: %s' #{head} >&2\")\n+    $stderr.puts \"warn: Are you sure you pushed '#{abbr(headref)}' there?\"\n     status = 1\n   end\n rescue CommandError\n-- \n1.8.4-fc\n"},{"id":"228390","messageId":"1380405849-13000-14-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 13/44] ruby: bind setup_git_directory()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:38Z","receivedAt":"2013-09-28T22:03:38Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n ruby.c           | 16 ++++++++++++++++\n t/t10000-ruby.sh | 13 +++++++++++++\n 2 files changed, 29 insertions(+)\n\ndiff --git a/ruby.c b/ruby.c\nindex 339e376..c2ef72e 100644\n--- a/ruby.c\n+++ b/ruby.c\n@@ -12,6 +12,21 @@ static inline VALUE sha1_to_str(const unsigned char *sha1)\n \treturn rb_str_new((const char *)sha1, 20);\n }\n \n+static inline VALUE cstr_to_str(const char *str)\n+{\n+\tif (str == NULL)\n+\t\treturn Qnil;\n+\treturn rb_str_new2(str);\n+}\n+\n+static VALUE git_rb_setup_git_directory(VALUE self)\n+{\n+\tint nongit_ok;\n+\tconst char *prefix;\n+\tprefix = setup_git_directory_gently(&nongit_ok);\n+\treturn rb_ary_new3(2, cstr_to_str(prefix), INT2FIX(nongit_ok));\n+}\n+\n static int for_each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data)\n {\n \tVALUE r;\n@@ -28,6 +43,7 @@ static VALUE git_rb_for_each_ref(void)\n \n static void git_ruby_init(void)\n {\n+\trb_define_global_function(\"setup_git_directory\", git_rb_setup_git_directory, 0);\n \trb_define_global_function(\"for_each_ref\", git_rb_for_each_ref, 0);\n }\n \ndiff --git a/t/t10000-ruby.sh b/t/t10000-ruby.sh\nindex eb03706..2098e29 100755\n--- a/t/t10000-ruby.sh\n+++ b/t/t10000-ruby.sh\n@@ -39,4 +39,17 @@ test_expect_success 'test for_each_ref()' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'test setup_git_directory()' '\n+\tmkdir t &&\n+\t(\n+\tcd t &&\n+\tgit ruby > ../actual <<-EOF\n+\tprefix, nongit_ok = setup_git_directory()\n+\tputs prefix\n+\tEOF\n+\t) &&\n+\techo \"t/\" > expected &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.8.4-fc\n"},{"id":"228392","messageId":"1380405849-13000-15-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 14/44] ruby: bind dwim_ref()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:39Z","receivedAt":"2013-09-28T22:03:39Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n ruby.c           | 11 +++++++++++\n t/t10000-ruby.sh | 11 +++++++++++\n 2 files changed, 22 insertions(+)\n\ndiff --git a/ruby.c b/ruby.c\nindex c2ef72e..0ab56de 100644\n--- a/ruby.c\n+++ b/ruby.c\n@@ -41,10 +41,21 @@ static VALUE git_rb_for_each_ref(void)\n \treturn INT2FIX(r);\n }\n \n+static VALUE git_rb_dwim_ref(VALUE self, VALUE name)\n+{\n+\tunsigned char buf[20];\n+\tchar *ref;\n+\tint r;\n+\n+\tr = dwim_ref(RSTRING_PTR(name), RSTRING_LEN(name), buf, &ref);\n+\treturn rb_ary_new3(3, sha1_to_str(buf), INT2NUM(r), cstr_to_str(ref));\n+}\n+\n static void git_ruby_init(void)\n {\n \trb_define_global_function(\"setup_git_directory\", git_rb_setup_git_directory, 0);\n \trb_define_global_function(\"for_each_ref\", git_rb_for_each_ref, 0);\n+\trb_define_global_function(\"dwim_ref\", git_rb_dwim_ref, 1);\n }\n \n static int run_ruby_command(const char *cmd, int argc, const char **argv)\ndiff --git a/t/t10000-ruby.sh b/t/t10000-ruby.sh\nindex 2098e29..d719b30 100755\n--- a/t/t10000-ruby.sh\n+++ b/t/t10000-ruby.sh\n@@ -52,4 +52,15 @@ test_expect_success 'test setup_git_directory()' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'test dwim_ref()' '\n+\tgit ruby > actual <<-EOF &&\n+\tsha1, num, ref = dwim_ref(\"HEAD\")\n+\tputs sha1_to_hex(sha1), num, ref\n+\tEOF\n+\tgit rev-parse -q --verify HEAD > expected &&\n+\techo 1 >> expected &&\n+\tgit rev-parse -q --verify --symbolic-full-name HEAD >> expected &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.8.4-fc\n"},{"id":"228391","messageId":"1380405849-13000-16-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 15/44] ruby: request-pull: use native dwim_ref()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:40Z","receivedAt":"2013-09-28T22:03:40Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    Our first real usage of the bindings doesn't look too different from what it\n    would look like in C.\n    \n    In Ruby all variables are pointers to objects, and there's no concept of\n    pointer to variables, so what in C looks like \"ret1 = func(var, &ret2)\", in\n    Ruby looks like \"ret1, ret2 = func(var)\".\n\n git-request-pull.rb | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nindex 9d3e9ab..416f880 100755\n--- a/git-request-pull.rb\n+++ b/git-request-pull.rb\n@@ -66,7 +66,7 @@ branch_name = branch_desc = nil\n \n usage unless base or url\n \n-headref = `git rev-parse -q --verify --symbolic-full-name \"#{head}\"`.chomp\n+_, _, headref = dwim_ref(head)\n \n if headref.start_with?('refs/heads')\n   branch_name = headref[11..-1]\n-- \n1.8.4-fc\n"},{"id":"228394","messageId":"1380405849-13000-17-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 16/44] ruby: bind git_config()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:41Z","receivedAt":"2013-09-28T22:03:41Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    Our first usage of Ruby blocks.\n    \n    Notice how the code inside the block is in the same scope as the code outside.\n    \n      cb_data = \"config\"\n      git_config() do |key, value|\n        puts \"%s: %s=%s\" % [cb_data, key, value]\n      end\n    \n    This invalidates the need for callback data, because the block has access to\n    those variables already.\n\n ruby.c           | 15 +++++++++++++++\n t/t10000-ruby.sh | 10 ++++++++++\n 2 files changed, 25 insertions(+)\n\ndiff --git a/ruby.c b/ruby.c\nindex 0ab56de..36de943 100644\n--- a/ruby.c\n+++ b/ruby.c\n@@ -51,11 +51,26 @@ static VALUE git_rb_dwim_ref(VALUE self, VALUE name)\n \treturn rb_ary_new3(3, sha1_to_str(buf), INT2NUM(r), cstr_to_str(ref));\n }\n \n+static int git_config_fn(const char *var, const char *value, void *cb_data)\n+{\n+\tVALUE r;\n+\tr = rb_yield_values(2, rb_str_new2(var), rb_str_new2(value));\n+\treturn r == Qfalse;\n+}\n+\n+static VALUE git_rb_git_config(VALUE self)\n+{\n+\tint r;\n+\tr = git_config(git_config_fn, NULL);\n+\treturn INT2FIX(r);\n+}\n+\n static void git_ruby_init(void)\n {\n \trb_define_global_function(\"setup_git_directory\", git_rb_setup_git_directory, 0);\n \trb_define_global_function(\"for_each_ref\", git_rb_for_each_ref, 0);\n \trb_define_global_function(\"dwim_ref\", git_rb_dwim_ref, 1);\n+\trb_define_global_function(\"git_config\", git_rb_git_config, 0);\n }\n \n static int run_ruby_command(const char *cmd, int argc, const char **argv)\ndiff --git a/t/t10000-ruby.sh b/t/t10000-ruby.sh\nindex d719b30..cdbf5d8 100755\n--- a/t/t10000-ruby.sh\n+++ b/t/t10000-ruby.sh\n@@ -63,4 +63,14 @@ test_expect_success 'test dwim_ref()' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'test git_config()' '\n+\tgit ruby > actual <<-EOF &&\n+\tgit_config() do |key, value|\n+\t  puts \"%s=%s\" % [key, value]\n+\tend\n+\tEOF\n+\tgit config --list > expected &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.8.4-fc\n"},{"id":"228393","messageId":"1380405849-13000-18-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 17/44] ruby: request-pull: use native git_config()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:42Z","receivedAt":"2013-09-28T22:03:42Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n git-request-pull.rb | 11 +++++++++--\n 1 file changed, 9 insertions(+), 2 deletions(-)\n\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nindex 416f880..0f85024 100755\n--- a/git-request-pull.rb\n+++ b/git-request-pull.rb\n@@ -14,6 +14,13 @@ EOF\n   exit 1\n end\n \n+def read_branch_desc(name)\n+  git_config() do |key, value|\n+    return value if key == \"branch.#{name}.description\"\n+  end\n+  return nil\n+end\n+\n def abbr(ref)\n   if (ref =~ %r{^refs/heads/(.*)} || ref =~ %r{^refs/(tags/.*)})\n     return $1\n@@ -70,8 +77,8 @@ _, _, headref = dwim_ref(head)\n \n if headref.start_with?('refs/heads')\n   branch_name = headref[11..-1]\n-  branch_desc = `git config \"branch.#{branch_name}.description\"`.chomp\n-  branch_name = nil if branch_desc.empty?\n+  branch_desc = read_branch_desc(branch_name)\n+  branch_name = nil if not branch_desc\n end\n \n tag_name = `git describe --exact \"#{head}^0\" 2>/dev/null`.chomp\n-- \n1.8.4-fc\n"},{"id":"228395","messageId":"1380405849-13000-19-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 18/44] ruby: bind read_ref()/peel_ref()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:43Z","receivedAt":"2013-09-28T22:03:43Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n ruby.c | 18 ++++++++++++++++++\n 1 file changed, 18 insertions(+)\n\ndiff --git a/ruby.c b/ruby.c\nindex 36de943..92c636c 100644\n--- a/ruby.c\n+++ b/ruby.c\n@@ -65,12 +65,30 @@ static VALUE git_rb_git_config(VALUE self)\n \treturn INT2FIX(r);\n }\n \n+static VALUE git_rb_read_ref(VALUE self, VALUE refname)\n+{\n+\tunsigned char sha1[20];\n+\tif (read_ref(RSTRING_PTR(refname), sha1))\n+\t\treturn Qnil;\n+\treturn sha1_to_str(sha1);\n+}\n+\n+static VALUE git_rb_peel_ref(VALUE self, VALUE refname)\n+{\n+\tunsigned char sha1[20];\n+\tif (peel_ref(RSTRING_PTR(refname), sha1))\n+\t\treturn Qnil;\n+\treturn sha1_to_str(sha1);\n+}\n+\n static void git_ruby_init(void)\n {\n \trb_define_global_function(\"setup_git_directory\", git_rb_setup_git_directory, 0);\n \trb_define_global_function(\"for_each_ref\", git_rb_for_each_ref, 0);\n \trb_define_global_function(\"dwim_ref\", git_rb_dwim_ref, 1);\n \trb_define_global_function(\"git_config\", git_rb_git_config, 0);\n+\trb_define_global_function(\"read_ref\", git_rb_read_ref, 1);\n+\trb_define_global_function(\"peel_ref\", git_rb_peel_ref, 1);\n }\n \n static int run_ruby_command(const char *cmd, int argc, const char **argv)\n-- \n1.8.4-fc\n"},{"id":"228396","messageId":"1380405849-13000-20-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 19/44] ruby: bind get_sha1()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:44Z","receivedAt":"2013-09-28T22:03:44Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n ruby.c           | 11 +++++++++++\n t/t10000-ruby.sh |  8 ++++++++\n 2 files changed, 19 insertions(+)\n\ndiff --git a/ruby.c b/ruby.c\nindex 92c636c..45111e6 100644\n--- a/ruby.c\n+++ b/ruby.c\n@@ -81,6 +81,16 @@ static VALUE git_rb_peel_ref(VALUE self, VALUE refname)\n \treturn sha1_to_str(sha1);\n }\n \n+static VALUE git_rb_get_sha1(VALUE self, VALUE name)\n+{\n+\tunsigned char buf[20];\n+\tint r;\n+\tr = get_sha1(RSTRING_PTR(name), buf);\n+\tif (r)\n+\t\treturn Qnil;\n+\treturn sha1_to_str(buf);\n+}\n+\n static void git_ruby_init(void)\n {\n \trb_define_global_function(\"setup_git_directory\", git_rb_setup_git_directory, 0);\n@@ -89,6 +99,7 @@ static void git_ruby_init(void)\n \trb_define_global_function(\"git_config\", git_rb_git_config, 0);\n \trb_define_global_function(\"read_ref\", git_rb_read_ref, 1);\n \trb_define_global_function(\"peel_ref\", git_rb_peel_ref, 1);\n+\trb_define_global_function(\"get_sha1\", git_rb_get_sha1, 1);\n }\n \n static int run_ruby_command(const char *cmd, int argc, const char **argv)\ndiff --git a/t/t10000-ruby.sh b/t/t10000-ruby.sh\nindex cdbf5d8..849a615 100755\n--- a/t/t10000-ruby.sh\n+++ b/t/t10000-ruby.sh\n@@ -73,4 +73,12 @@ test_expect_success 'test git_config()' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'test get_sha1()' '\n+\tgit ruby > actual <<-EOF &&\n+\tputs sha1_to_hex(get_sha1(\"HEAD\"))\n+\tEOF\n+\tgit rev-parse -q --verify HEAD > expected &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.8.4-fc\n"},{"id":"228397","messageId":"1380405849-13000-21-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 20/44] ruby: request-pull: simplify tag fetching","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:45Z","receivedAt":"2013-09-28T22:03:45Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    For the moment we use \"str[10..-1]\" to denote the equivalent of \"str + 10\" in\n    C, even though Ruby has nicer constructs than that.\n\n git-request-pull.rb | 15 ++++++++++++---\n 1 file changed, 12 insertions(+), 3 deletions(-)\n\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nindex 0f85024..25dc04d 100755\n--- a/git-request-pull.rb\n+++ b/git-request-pull.rb\n@@ -21,6 +21,15 @@ def read_branch_desc(name)\n   return nil\n end\n \n+def describe(rev)\n+  for_each_ref() do |name, sha1, flags|\n+    next unless name.start_with?('refs/tags/')\n+    next unless peel_ref(name) == get_sha1(rev)\n+    return name[10..-1]\n+  end\n+  return nil\n+end\n+\n def abbr(ref)\n   if (ref =~ %r{^refs/heads/(.*)} || ref =~ %r{^refs/(tags/.*)})\n     return $1\n@@ -81,7 +90,7 @@ if headref.start_with?('refs/heads')\n   branch_name = nil if not branch_desc\n end\n \n-tag_name = `git describe --exact \"#{head}^0\" 2>/dev/null`.chomp\n+tag_name = describe(head)\n \n baserev = `git rev-parse --verify --quiet \"#{base}\"^0`.chomp\n die \"Not a valid revision: #{base}\" if baserev.empty?\n@@ -116,7 +125,7 @@ for you to fetch changes up to %H:\n     puts branch_desc\n   end\n \n-  if not tag_name.empty?\n+  if tag_name\n     if ref != \"tags/#{tag_name}\"\n       $stderr.puts \"warn: You locally have #{tag_name} but it does not (yet)\"\n       $stderr.puts \"warn: appear to be at #{url}\"\n@@ -126,7 +135,7 @@ for you to fetch changes up to %H:\n     puts\n   end\n \n-  if branch_name || ! tag_name.empty?\n+  if branch_name || tag_name\n     puts \"----------------------------------------------------------------\"\n   end\n \n-- \n1.8.4-fc\n"},{"id":"228400","messageId":"1380405849-13000-22-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 21/44] ruby: request-pull: use get_sha1()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:46Z","receivedAt":"2013-09-28T22:03:46Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    To minimize the changes in the rest of the script, the sha1 is converted by to\n    a hex.\n\n git-request-pull.rb | 11 +++++++----\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nindex 25dc04d..9267147 100755\n--- a/git-request-pull.rb\n+++ b/git-request-pull.rb\n@@ -92,11 +92,14 @@ end\n \n tag_name = describe(head)\n \n-baserev = `git rev-parse --verify --quiet \"#{base}\"^0`.chomp\n-die \"Not a valid revision: #{base}\" if baserev.empty?\n+baserev = get_sha1(\"#{base}^0\")\n+die \"Not a valid revision: #{base}\" unless baserev\n \n-headrev = `git rev-parse --verify --quiet \"#{head}\"^0`.chomp\n-die \"Not a valid revision: #{head}\" if headrev.empty?\n+headrev = get_sha1(\"#{head}^0\")\n+die \"Not a valid revision: #{head}\" unless headrev\n+\n+baserev = sha1_to_hex(baserev)\n+headrev = sha1_to_hex(headrev)\n \n merge_base = `git merge-base #{baserev} #{headrev}`.chomp\n die \"No commits in common between #{base} and #{head}\" unless $?.success?\n-- \n1.8.4-fc\n"},{"id":"228398","messageId":"1380405849-13000-23-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 22/44] ruby: add Commit class","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:47Z","receivedAt":"2013-09-28T22:03:47Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    The Commit class inherits from the Object class, so commit.sha1() just works,\n    even though it's an Object method.\n    \n    For now we don't define initializers or allocators, so Git::Commit.new() cannot\n    really be used, but it's not needed at this point. These would be demonstrated\n    later in the series.\n\n ruby.c           | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n t/t10000-ruby.sh | 21 ++++++++++++++\n 2 files changed, 107 insertions(+)\n\ndiff --git a/ruby.c b/ruby.c\nindex 45111e6..53afa32 100644\n--- a/ruby.c\n+++ b/ruby.c\n@@ -1,12 +1,17 @@\n #include \"cache.h\"\n #include \"exec_cmd.h\"\n #include \"refs.h\"\n+#include \"object.h\"\n+#include \"commit.h\"\n \n #undef NORETURN\n #undef PATH_SEP\n \n #include <ruby.h>\n \n+static VALUE git_rb_object;\n+static VALUE git_rb_commit;\n+\n static inline VALUE sha1_to_str(const unsigned char *sha1)\n {\n \treturn rb_str_new((const char *)sha1, 20);\n@@ -19,6 +24,18 @@ static inline VALUE cstr_to_str(const char *str)\n \treturn rb_str_new2(str);\n }\n \n+static inline char *str_to_cstr(VALUE str)\n+{\n+\tif (str == Qnil)\n+\t\treturn NULL;\n+\treturn RSTRING_PTR(str);\n+}\n+\n+static inline unsigned char *str_to_sha1(VALUE str)\n+{\n+\treturn (unsigned char *)str_to_cstr(str);\n+}\n+\n static VALUE git_rb_setup_git_directory(VALUE self)\n {\n \tint nongit_ok;\n@@ -91,8 +108,67 @@ static VALUE git_rb_get_sha1(VALUE self, VALUE name)\n \treturn sha1_to_str(buf);\n }\n \n+static VALUE git_rb_object_get(VALUE class, VALUE id)\n+{\n+\tstruct object *object;\n+\tobject = parse_object(str_to_sha1(id));\n+\treturn Data_Wrap_Struct(git_rb_object, NULL, NULL, object);\n+}\n+\n+static VALUE git_rb_commit_get(VALUE class, VALUE id)\n+{\n+\tstruct object *object;\n+\tobject = parse_object(str_to_sha1(id));\n+\tif (!object || object->type != OBJ_COMMIT)\n+\t\treturn Qnil;\n+\treturn Data_Wrap_Struct(git_rb_commit, NULL, NULL, object);\n+}\n+\n+static VALUE git_rb_object_sha1(VALUE self)\n+{\n+\tstruct object *object;\n+\tData_Get_Struct(self, struct object, object);\n+\treturn sha1_to_str(object->sha1);\n+}\n+\n+static VALUE git_rb_object_type(VALUE self)\n+{\n+\tstruct object *object;\n+\tData_Get_Struct(self, struct object, object);\n+\treturn INT2FIX(object->type);\n+}\n+\n+static VALUE git_rb_object_to_s(VALUE self)\n+{\n+\tstruct object *object;\n+\tData_Get_Struct(self, struct object, object);\n+\treturn rb_str_new2(sha1_to_hex(object->sha1));\n+}\n+\n+static VALUE git_rb_commit_buffer(VALUE self)\n+{\n+\tstruct commit *commit;\n+\tData_Get_Struct(self, struct commit, commit);\n+\treturn cstr_to_str(commit->buffer);\n+}\n+\n static void git_ruby_init(void)\n {\n+\tVALUE mod;\n+\n+\tmod = rb_define_module(\"Git\");\n+\n+\trb_define_global_const(\"OBJ_BAD\", INT2FIX(OBJ_BAD));\n+\trb_define_global_const(\"OBJ_NONE\", INT2FIX(OBJ_NONE));\n+\trb_define_global_const(\"OBJ_COMMIT\", INT2FIX(OBJ_COMMIT));\n+\trb_define_global_const(\"OBJ_TREE\", INT2FIX(OBJ_TREE));\n+\trb_define_global_const(\"OBJ_BLOB\", INT2FIX(OBJ_BLOB));\n+\trb_define_global_const(\"OBJ_TAG\", INT2FIX(OBJ_TAG));\n+\trb_define_global_const(\"OBJ_OFS_DELTA\", INT2FIX(OBJ_OFS_DELTA));\n+\trb_define_global_const(\"OBJ_REF_DELTA\", INT2FIX(OBJ_REF_DELTA));\n+\trb_define_global_const(\"OBJ_ANY\", INT2FIX(OBJ_ANY));\n+\trb_define_global_const(\"OBJ_MAX\", INT2FIX(OBJ_MAX));\n+\n \trb_define_global_function(\"setup_git_directory\", git_rb_setup_git_directory, 0);\n \trb_define_global_function(\"for_each_ref\", git_rb_for_each_ref, 0);\n \trb_define_global_function(\"dwim_ref\", git_rb_dwim_ref, 1);\n@@ -100,6 +176,16 @@ static void git_ruby_init(void)\n \trb_define_global_function(\"read_ref\", git_rb_read_ref, 1);\n \trb_define_global_function(\"peel_ref\", git_rb_peel_ref, 1);\n \trb_define_global_function(\"get_sha1\", git_rb_get_sha1, 1);\n+\n+\tgit_rb_object = rb_define_class_under(mod, \"Object\", rb_cData);\n+\trb_define_singleton_method(git_rb_object, \"get\", git_rb_object_get, 1);\n+\trb_define_method(git_rb_object, \"sha1\", git_rb_object_sha1, 0);\n+\trb_define_method(git_rb_object, \"type\", git_rb_object_type, 0);\n+\trb_define_method(git_rb_object, \"to_s\", git_rb_object_to_s, 0);\n+\n+\tgit_rb_commit = rb_define_class_under(mod, \"Commit\", git_rb_object);\n+\trb_define_singleton_method(git_rb_commit, \"get\", git_rb_commit_get, 1);\n+\trb_define_method(git_rb_commit, \"buffer\", git_rb_commit_buffer, 0);\n }\n \n static int run_ruby_command(const char *cmd, int argc, const char **argv)\ndiff --git a/t/t10000-ruby.sh b/t/t10000-ruby.sh\nindex 849a615..e5a397c 100755\n--- a/t/t10000-ruby.sh\n+++ b/t/t10000-ruby.sh\n@@ -81,4 +81,25 @@ test_expect_success 'test get_sha1()' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'test Object' '\n+\tgit ruby > actual <<-EOF &&\n+\tobject = Git::Object.get(get_sha1(\"HEAD\"))\n+\tputs object, object.type == OBJ_COMMIT, sha1_to_hex(object.sha1)\n+\tEOF\n+\tgit rev-parse -q --verify HEAD > expected &&\n+\techo \"true\" >> expected &&\n+\tgit rev-parse -q --verify HEAD >> expected &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'test Commit' '\n+\tgit ruby > actual <<-EOF &&\n+\tcommit = Git::Commit.get(get_sha1(\"HEAD\"))\n+\tputs commit, commit.buffer\n+\tEOF\n+\tgit rev-parse -q --verify HEAD > expected &&\n+\tgit cat-file commit HEAD >> expected &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.8.4-fc\n"},{"id":"228399","messageId":"1380405849-13000-24-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 23/44] ruby: bind get_merge_bases()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:48Z","receivedAt":"2013-09-28T22:03:48Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    We could call it get_merge_bases_many(), but the fact that it's awkward to\n    pass arrays in C is a limitation we don't have in Ruby. We can pass as many\n    commits as we want easily.\n\n ruby.c | 36 ++++++++++++++++++++++++++++++++++++\n 1 file changed, 36 insertions(+)\n\ndiff --git a/ruby.c b/ruby.c\nindex 53afa32..cc8fe0e 100644\n--- a/ruby.c\n+++ b/ruby.c\n@@ -11,6 +11,7 @@\n \n static VALUE git_rb_object;\n static VALUE git_rb_commit;\n+static VALUE git_rb_commit_list;\n \n static inline VALUE sha1_to_str(const unsigned char *sha1)\n {\n@@ -152,6 +153,36 @@ static VALUE git_rb_commit_buffer(VALUE self)\n \treturn cstr_to_str(commit->buffer);\n }\n \n+static VALUE git_rb_commit_list_each(VALUE self)\n+{\n+\tstruct commit_list *e, *list;\n+\tData_Get_Struct(self, struct commit_list, list);\n+\n+\tfor (e = list; e; e = e->next) {\n+\t\tVALUE c;\n+\t\tc = Data_Wrap_Struct(git_rb_commit, NULL, NULL, e->item);\n+\t\trb_yield(c);\n+\t}\n+\n+\treturn self;\n+}\n+\n+static VALUE git_rb_get_merge_bases(VALUE self, VALUE commits, VALUE cleanup)\n+{\n+\tstruct commit *g_commits[RARRAY_LEN(commits)];\n+\tstruct commit_list *result;\n+\tint i;\n+\n+\tfor (i = 0; i < RARRAY_LEN(commits); i++) {\n+\t\tVALUE commit = RARRAY_PTR(commits)[i];\n+\t\tData_Get_Struct(commit, struct commit, g_commits[i]);\n+\t}\n+\tresult = get_merge_bases_many(g_commits[0], RARRAY_LEN(commits) - 1, g_commits + 1, NUM2INT(cleanup));\n+\tif (!result)\n+\t\treturn Qnil;\n+\treturn Data_Wrap_Struct(git_rb_commit_list, NULL, NULL, result);\n+}\n+\n static void git_ruby_init(void)\n {\n \tVALUE mod;\n@@ -176,6 +207,7 @@ static void git_ruby_init(void)\n \trb_define_global_function(\"read_ref\", git_rb_read_ref, 1);\n \trb_define_global_function(\"peel_ref\", git_rb_peel_ref, 1);\n \trb_define_global_function(\"get_sha1\", git_rb_get_sha1, 1);\n+\trb_define_global_function(\"get_merge_bases\", git_rb_get_merge_bases, 2);\n \n \tgit_rb_object = rb_define_class_under(mod, \"Object\", rb_cData);\n \trb_define_singleton_method(git_rb_object, \"get\", git_rb_object_get, 1);\n@@ -186,6 +218,10 @@ static void git_ruby_init(void)\n \tgit_rb_commit = rb_define_class_under(mod, \"Commit\", git_rb_object);\n \trb_define_singleton_method(git_rb_commit, \"get\", git_rb_commit_get, 1);\n \trb_define_method(git_rb_commit, \"buffer\", git_rb_commit_buffer, 0);\n+\n+\tgit_rb_commit_list = rb_define_class_under(mod, \"CommitList\", rb_cData);\n+\trb_include_module(git_rb_commit_list, rb_mEnumerable);\n+\trb_define_method(git_rb_commit_list, \"each\", git_rb_commit_list_each, 0);\n }\n \n static int run_ruby_command(const char *cmd, int argc, const char **argv)\n-- \n1.8.4-fc\n"},{"id":"228402","messageId":"1380405849-13000-25-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 24/44] ruby: request-pull: use get_merge_bases()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:49Z","receivedAt":"2013-09-28T22:03:49Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    Now headrev and baserev become Git::Commit objects, fortunately\n    'puts(\"#{commit}\")' or 'printf(\"%s\\n\", commit) convert the commit to a String\n    (commit.to_s()), which is the hex sha1, so the rest of the script doesn't need\n    to be changed.\n\n git-request-pull.rb | 12 +++++++-----\n 1 file changed, 7 insertions(+), 5 deletions(-)\n\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nindex 9267147..be4947d 100755\n--- a/git-request-pull.rb\n+++ b/git-request-pull.rb\n@@ -98,13 +98,15 @@ die \"Not a valid revision: #{base}\" unless baserev\n headrev = get_sha1(\"#{head}^0\")\n die \"Not a valid revision: #{head}\" unless headrev\n \n-baserev = sha1_to_hex(baserev)\n-headrev = sha1_to_hex(headrev)\n+baserev = Git::Commit.get(baserev)\n+headrev = Git::Commit.get(headrev)\n \n-merge_base = `git merge-base #{baserev} #{headrev}`.chomp\n-die \"No commits in common between #{base} and #{head}\" unless $?.success?\n+merge_bases = get_merge_bases([baserev, headrev], 0);\n+die \"No commits in common between #{base} and #{head}\" unless merge_bases\n \n-ref = get_ref(url, headref != \"HEAD\" ? headref : nil, headrev, tag_name)\n+merge_base = sha1_to_hex(merge_bases.first.sha1)\n+\n+ref = get_ref(url, headref != \"HEAD\" ? headref : nil, headrev.to_s, tag_name)\n url = `git ls-remote --get-url \"#{url}\"`.chomp\n \n begin\n-- \n1.8.4-fc\n"},{"id":"228401","messageId":"1380405849-13000-26-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 25/44] ruby: request-pull: trivial cleanups","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:50Z","receivedAt":"2013-09-28T22:03:50Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    The fist override of a native class, which is one of the strong points of Ruby.\n    \n    We could use skip_prefix(str, prefix) like in C, but str.skip_prefix(prefix) is\n    more natrual in Ruby.\n\n git-rb-setup.rb     |  6 ++++++\n git-request-pull.rb | 36 ++++++++++++++++++------------------\n 2 files changed, 24 insertions(+), 18 deletions(-)\n\ndiff --git a/git-rb-setup.rb b/git-rb-setup.rb\nindex 92998a2..6f283da 100644\n--- a/git-rb-setup.rb\n+++ b/git-rb-setup.rb\n@@ -26,3 +26,9 @@ def run(cmd, *args)\n   system(*cmd, *args)\n   raise CommandError.new(cmd) unless $?.success?\n end\n+\n+class String\n+  def skip_prefix(prefix)\n+    return self[prefix.length..-1]\n+  end\n+end\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nindex be4947d..79a26dc 100755\n--- a/git-request-pull.rb\n+++ b/git-request-pull.rb\n@@ -25,7 +25,7 @@ def describe(rev)\n   for_each_ref() do |name, sha1, flags|\n     next unless name.start_with?('refs/tags/')\n     next unless peel_ref(name) == get_sha1(rev)\n-    return name[10..-1]\n+    return name.skip_prefix('refs/tags/')\n   end\n   return nil\n end\n@@ -43,17 +43,17 @@ end\n # Otherwise, if a branch with the same name as $head exists at the remote\n # and their values match, use that instead.\n #\n-# Otherwise find a random ref that matches $headrev.\n+# Otherwise find a random ref that matches $head_id.\n \n-def get_ref(url, headref, headrev, tag_name)\n+def get_ref(url, head_ref, head_id, tag_name)\n   found = nil\n   IO.popen(%[git ls-remote \"#{url}\"]) do |out|\n     out.each do |l|\n       sha1, ref, deref = l.scan(/^(\\S+)\\s+(\\S+?)(\\^\\{\\})?$/).first\n-      next unless sha1 == headrev\n+      next unless sha1 == head_id\n       found = abbr(ref)\n       break if (deref && ref == \"refs/tags/#{tag_name}\")\n-      break if ref == headref\n+      break if ref == head_ref\n     end\n   end\n   return found\n@@ -82,31 +82,31 @@ branch_name = branch_desc = nil\n \n usage unless base or url\n \n-_, _, headref = dwim_ref(head)\n+_, _, head_ref = dwim_ref(head)\n \n-if headref.start_with?('refs/heads')\n-  branch_name = headref[11..-1]\n+if head_ref.start_with?('refs/heads')\n+  branch_name = head_ref[11..-1]\n   branch_desc = read_branch_desc(branch_name)\n   branch_name = nil if not branch_desc\n end\n \n tag_name = describe(head)\n \n-baserev = get_sha1(\"#{base}^0\")\n-die \"Not a valid revision: #{base}\" unless baserev\n+base_id = get_sha1(\"#{base}^0\")\n+die \"Not a valid revision: #{base}\" unless base_id\n \n-headrev = get_sha1(\"#{head}^0\")\n-die \"Not a valid revision: #{head}\" unless headrev\n+head_id = get_sha1(\"#{head}^0\")\n+die \"Not a valid revision: #{head}\" unless head_id\n \n-baserev = Git::Commit.get(baserev)\n-headrev = Git::Commit.get(headrev)\n+base_commit = Git::Commit.get(base_id)\n+head_commit = Git::Commit.get(head_id)\n \n-merge_bases = get_merge_bases([baserev, headrev], 0);\n+merge_bases = get_merge_bases([base_commit, head_commit], 0);\n die \"No commits in common between #{base} and #{head}\" unless merge_bases\n \n merge_base = sha1_to_hex(merge_bases.first.sha1)\n \n-ref = get_ref(url, headref != \"HEAD\" ? headref : nil, headrev.to_s, tag_name)\n+ref = get_ref(url, head_ref != \"HEAD\" ? head_ref : nil, head_commit.to_s, tag_name)\n url = `git ls-remote --get-url \"#{url}\"`.chomp\n \n begin\n@@ -122,7 +122,7 @@ for you to fetch changes up to %H:\n \n   %s (%ci)\n \n-----------------------------------------------------------------' #{headrev}])\n+----------------------------------------------------------------' #{head_commit}])\n \n   if branch_name\n     puts \"(from the branch description for #{branch_name} local branch)\"\n@@ -150,7 +150,7 @@ for you to fetch changes up to %H:\n   if ! ref\n     $stderr.puts \"warn: No branch of #{url} is at:\"\n     run(\"git show -s --format='warn:   %h: %s' #{head} >&2\")\n-    $stderr.puts \"warn: Are you sure you pushed '#{abbr(headref)}' there?\"\n+    $stderr.puts \"warn: Are you sure you pushed '#{abbr(head_ref)}' there?\"\n     status = 1\n   end\n rescue CommandError\n-- \n1.8.4-fc\n"},{"id":"228403","messageId":"1380405849-13000-27-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 26/44] ruby: bind remote and transport stuff","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:51Z","receivedAt":"2013-09-28T22:03:51Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    Only the few fields that will be needed later on.\n\n ruby.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 89 insertions(+)\n\ndiff --git a/ruby.c b/ruby.c\nindex cc8fe0e..ee548dc 100644\n--- a/ruby.c\n+++ b/ruby.c\n@@ -3,6 +3,8 @@\n #include \"refs.h\"\n #include \"object.h\"\n #include \"commit.h\"\n+#include \"remote.h\"\n+#include \"transport.h\"\n \n #undef NORETURN\n #undef PATH_SEP\n@@ -12,6 +14,9 @@\n static VALUE git_rb_object;\n static VALUE git_rb_commit;\n static VALUE git_rb_commit_list;\n+static VALUE git_rb_remote;\n+static VALUE git_rb_transport;\n+static VALUE git_rb_ref;\n \n static inline VALUE sha1_to_str(const unsigned char *sha1)\n {\n@@ -183,6 +188,76 @@ static VALUE git_rb_get_merge_bases(VALUE self, VALUE commits, VALUE cleanup)\n \treturn Data_Wrap_Struct(git_rb_commit_list, NULL, NULL, result);\n }\n \n+static VALUE git_rb_remote_get(VALUE self, VALUE name)\n+{\n+\tstruct remote *remote;\n+\tremote = remote_get(RSTRING_PTR(name));\n+\tif (!remote)\n+\t\treturn Qnil;\n+\treturn Data_Wrap_Struct(git_rb_remote, NULL, NULL, remote);\n+}\n+\n+static VALUE git_rb_remote_url(VALUE self)\n+{\n+\tstruct remote *remote;\n+\tVALUE url;\n+\tint i;\n+\n+\tData_Get_Struct(self, struct remote, remote);\n+\turl = rb_ary_new2(remote->url_nr);\n+\tfor (i = 0; i < remote->url_nr; i++)\n+\t\trb_ary_store(url, i, rb_str_new2(remote->url[i]));\n+\treturn url;\n+}\n+\n+static VALUE git_rb_transport_get(VALUE self, VALUE remote, VALUE url)\n+{\n+\tstruct transport *transport;\n+\tstruct remote *g_remote;\n+\tData_Get_Struct(remote, struct remote, g_remote);\n+\ttransport = transport_get(g_remote, str_to_cstr(url));\n+\tif (!transport)\n+\t\treturn Qnil;\n+\treturn Data_Wrap_Struct(git_rb_transport, NULL, transport_disconnect, transport);\n+}\n+\n+static VALUE git_rb_transport_get_remote_refs(VALUE self)\n+{\n+\tstruct transport *transport;\n+\tconst struct ref *ref;\n+\tData_Get_Struct(self, struct transport, transport);\n+\tref = transport_get_remote_refs(transport);\n+\treturn Data_Wrap_Struct(git_rb_ref, NULL, NULL, (void *)ref);\n+}\n+\n+static VALUE git_rb_ref_each(VALUE self)\n+{\n+\tstruct ref *e, *ref;\n+\tData_Get_Struct(self, struct ref, ref);\n+\n+\tfor (e = ref; e; e = e->next) {\n+\t\tVALUE c;\n+\t\tc = Data_Wrap_Struct(git_rb_ref, NULL, NULL, e);\n+\t\trb_yield(c);\n+\t}\n+\n+\treturn self;\n+}\n+\n+static VALUE git_rb_ref_name(VALUE self)\n+{\n+\tstruct ref *ref;\n+\tData_Get_Struct(self, struct ref, ref);\n+\treturn rb_str_new2(ref->name);\n+}\n+\n+static VALUE git_rb_ref_old_sha1(VALUE self)\n+{\n+\tstruct ref *ref;\n+\tData_Get_Struct(self, struct ref, ref);\n+\treturn sha1_to_str(ref->old_sha1);\n+}\n+\n static void git_ruby_init(void)\n {\n \tVALUE mod;\n@@ -208,6 +283,8 @@ static void git_ruby_init(void)\n \trb_define_global_function(\"peel_ref\", git_rb_peel_ref, 1);\n \trb_define_global_function(\"get_sha1\", git_rb_get_sha1, 1);\n \trb_define_global_function(\"get_merge_bases\", git_rb_get_merge_bases, 2);\n+\trb_define_global_function(\"remote_get\", git_rb_remote_get, 1);\n+\trb_define_global_function(\"transport_get\", git_rb_transport_get, 2);\n \n \tgit_rb_object = rb_define_class_under(mod, \"Object\", rb_cData);\n \trb_define_singleton_method(git_rb_object, \"get\", git_rb_object_get, 1);\n@@ -222,6 +299,18 @@ static void git_ruby_init(void)\n \tgit_rb_commit_list = rb_define_class_under(mod, \"CommitList\", rb_cData);\n \trb_include_module(git_rb_commit_list, rb_mEnumerable);\n \trb_define_method(git_rb_commit_list, \"each\", git_rb_commit_list_each, 0);\n+\n+\tgit_rb_remote = rb_define_class_under(mod, \"Remote\", rb_cData);\n+\trb_define_method(git_rb_remote, \"url\", git_rb_remote_url, 0);\n+\n+\tgit_rb_transport = rb_define_class_under(mod, \"Transport\", rb_cData);\n+\trb_define_method(git_rb_transport, \"get_remote_refs\", git_rb_transport_get_remote_refs, 0);\n+\n+\tgit_rb_ref = rb_define_class_under(mod, \"Ref\", rb_cData);\n+\trb_include_module(git_rb_ref, rb_mEnumerable);\n+\trb_define_method(git_rb_ref, \"each\", git_rb_ref_each, 0);\n+\trb_define_method(git_rb_ref, \"name\", git_rb_ref_name, 0);\n+\trb_define_method(git_rb_ref, \"old_sha1\", git_rb_ref_old_sha1, 0);\n }\n \n static int run_ruby_command(const char *cmd, int argc, const char **argv)\n-- \n1.8.4-fc\n"},{"id":"228404","messageId":"1380405849-13000-28-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 27/44] ruby: request-pull: use native remote and transport","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:52Z","receivedAt":"2013-09-28T22:03:52Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n git-request-pull.rb | 24 +++++++++++++-----------\n 1 file changed, 13 insertions(+), 11 deletions(-)\n\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nindex 79a26dc..869f160 100755\n--- a/git-request-pull.rb\n+++ b/git-request-pull.rb\n@@ -45,16 +45,15 @@ end\n #\n # Otherwise find a random ref that matches $head_id.\n \n-def get_ref(url, head_ref, head_id, tag_name)\n+def get_ref(transport, head_ref, head_id, tag_name)\n   found = nil\n-  IO.popen(%[git ls-remote \"#{url}\"]) do |out|\n-    out.each do |l|\n-      sha1, ref, deref = l.scan(/^(\\S+)\\s+(\\S+?)(\\^\\{\\})?$/).first\n-      next unless sha1 == head_id\n-      found = abbr(ref)\n-      break if (deref && ref == \"refs/tags/#{tag_name}\")\n-      break if ref == head_ref\n-    end\n+  transport.get_remote_refs().each do |e|\n+    sha1 = e.old_sha1\n+    next unless sha1 == head_id\n+    ref, deref = e.name.scan(/^(\\S+?)(\\^\\{\\})?$/).first\n+    found = abbr(ref)\n+    break if (deref && ref == \"refs/tags/#{tag_name}\")\n+    break if ref == head_ref\n   end\n   return found\n end\n@@ -106,8 +105,11 @@ die \"No commits in common between #{base} and #{head}\" unless merge_bases\n \n merge_base = sha1_to_hex(merge_bases.first.sha1)\n \n-ref = get_ref(url, head_ref != \"HEAD\" ? head_ref : nil, head_commit.to_s, tag_name)\n-url = `git ls-remote --get-url \"#{url}\"`.chomp\n+remote = remote_get(url)\n+transport = transport_get(remote, nil)\n+\n+ref = get_ref(transport, head_ref != \"HEAD\" ? head_ref : nil, head_id, tag_name)\n+url = remote.url.first\n \n begin\n   run(%[git show -s --format='The following changes since commit %H:\n-- \n1.8.4-fc\n"},{"id":"228407","messageId":"1380405849-13000-29-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 28/44] ruby: bind find_unique_abbrev()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:53Z","receivedAt":"2013-09-28T22:03:53Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n ruby.c | 10 ++++++++++\n 1 file changed, 10 insertions(+)\n\ndiff --git a/ruby.c b/ruby.c\nindex ee548dc..7496c18 100644\n--- a/ruby.c\n+++ b/ruby.c\n@@ -258,6 +258,13 @@ static VALUE git_rb_ref_old_sha1(VALUE self)\n \treturn sha1_to_str(ref->old_sha1);\n }\n \n+static VALUE git_rb_find_unique_abbrev(VALUE self, VALUE sha1, VALUE len)\n+{\n+\tconst char *abbrev;\n+\tabbrev = find_unique_abbrev(str_to_sha1(sha1), NUM2INT(len));\n+\treturn rb_str_new2(abbrev);\n+}\n+\n static void git_ruby_init(void)\n {\n \tVALUE mod;\n@@ -275,6 +282,8 @@ static void git_ruby_init(void)\n \trb_define_global_const(\"OBJ_ANY\", INT2FIX(OBJ_ANY));\n \trb_define_global_const(\"OBJ_MAX\", INT2FIX(OBJ_MAX));\n \n+\trb_define_global_const(\"DEFAULT_ABBREV\", INT2FIX(DEFAULT_ABBREV));\n+\n \trb_define_global_function(\"setup_git_directory\", git_rb_setup_git_directory, 0);\n \trb_define_global_function(\"for_each_ref\", git_rb_for_each_ref, 0);\n \trb_define_global_function(\"dwim_ref\", git_rb_dwim_ref, 1);\n@@ -285,6 +294,7 @@ static void git_ruby_init(void)\n \trb_define_global_function(\"get_merge_bases\", git_rb_get_merge_bases, 2);\n \trb_define_global_function(\"remote_get\", git_rb_remote_get, 1);\n \trb_define_global_function(\"transport_get\", git_rb_transport_get, 2);\n+\trb_define_global_function(\"find_unique_abbrev\", git_rb_find_unique_abbrev, 2);\n \n \tgit_rb_object = rb_define_class_under(mod, \"Object\", rb_cData);\n \trb_define_singleton_method(git_rb_object, \"get\", git_rb_object_get, 1);\n-- \n1.8.4-fc\n"},{"id":"228406","messageId":"1380405849-13000-30-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 29/44] ruby: request-pull: use native commit info","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:54Z","receivedAt":"2013-09-28T22:03:54Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    We could use a bit more of the internal parsing in C, or we could export more,\n    but there's no need to, parse_buffer() is relatively simple in Ruby.\n\n git-request-pull.rb | 47 +++++++++++++++++++++++++++++++++++++----------\n 1 file changed, 37 insertions(+), 10 deletions(-)\n\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nindex 869f160..5f40901 100755\n--- a/git-request-pull.rb\n+++ b/git-request-pull.rb\n@@ -1,5 +1,7 @@\n #!git ruby\n \n+require 'date'\n+\n ENV['GIT_PAGER'] =\n \n patch = ''\n@@ -58,6 +60,25 @@ def get_ref(transport, head_ref, head_id, tag_name)\n   return found\n end\n \n+def parse_buffer(buffer)\n+  summary = []\n+  date = msg = nil\n+  header, body = buffer.split(\"\\n\\n\", 2)\n+  header.each_line do |line|\n+    case line\n+    when /^committer ([^<>]+) <(\\S+)> (.+)$/\n+      date = DateTime.strptime($3, '%s %z')\n+    end\n+  end\n+  body.each_line do |l|\n+    break if (l.strip.empty?)\n+    summary << l.chomp\n+  end\n+  summary = summary.join(' ')\n+  date = date.strftime('%F %T %z')\n+  return [summary, date]\n+end\n+\n until ARGV.empty?\n   case ARGV.first\n   when '-p'\n@@ -103,7 +124,8 @@ head_commit = Git::Commit.get(head_id)\n merge_bases = get_merge_bases([base_commit, head_commit], 0);\n die \"No commits in common between #{base} and #{head}\" unless merge_bases\n \n-merge_base = sha1_to_hex(merge_bases.first.sha1)\n+merge_base_id = merge_bases.first.sha1\n+merge_base_commit = Git::Commit.get(merge_base_id)\n \n remote = remote_get(url)\n transport = transport_get(remote, nil)\n@@ -111,20 +133,25 @@ transport = transport_get(remote, nil)\n ref = get_ref(transport, head_ref != \"HEAD\" ? head_ref : nil, head_id, tag_name)\n url = remote.url.first\n \n+merge_base_summary, merge_base_date = parse_buffer(merge_base_commit.buffer)\n+head_summary, head_date = parse_buffer(head_commit.buffer)\n+\n begin\n-  run(%[git show -s --format='The following changes since commit %H:\n+  puts \"The following changes since commit %s:\n \n-  %s (%ci)\n+  %s (%s)\n \n are available in the git repository at:\n-' #{merge_base}])\n+\n+\" % [merge_base_commit, merge_base_summary, merge_base_date]\n   puts \"  #{url}\" + (ref ? \" #{ref}\" : \"\")\n-  run(%[git show -s --format='\n-for you to fetch changes up to %H:\n+  puts \"\n+for you to fetch changes up to %s:\n \n-  %s (%ci)\n+  %s (%s)\n \n-----------------------------------------------------------------' #{head_commit}])\n+----------------------------------------------------------------\n+\" % [head_commit, head_summary, head_date]\n \n   if branch_name\n     puts \"(from the branch description for #{branch_name} local branch)\"\n@@ -147,11 +174,11 @@ for you to fetch changes up to %H:\n   end\n \n   run(%[git shortlog ^#{base} #{head}])\n-  run(%[git diff -M --stat --summary #{patch} ^#{merge_base} #{head}])\n+  run(%[git diff -M --stat --summary #{patch} ^#{merge_base_commit} #{head}])\n \n   if ! ref\n     $stderr.puts \"warn: No branch of #{url} is at:\"\n-    run(\"git show -s --format='warn:   %h: %s' #{head} >&2\")\n+    $stderr.puts \"warn:   %s: %s'\" % [find_unique_abbrev(head_id, DEFAULT_ABBREV), head_summary]\n     $stderr.puts \"warn: Are you sure you pushed '#{abbr(head_ref)}' there?\"\n     status = 1\n   end\n-- \n1.8.4-fc\n"},{"id":"228405","messageId":"1380405849-13000-31-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 30/44] ruby: bind read_sha1_file()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:55Z","receivedAt":"2013-09-28T22:03:55Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n ruby.c | 13 +++++++++++++\n 1 file changed, 13 insertions(+)\n\ndiff --git a/ruby.c b/ruby.c\nindex 7496c18..360515f 100644\n--- a/ruby.c\n+++ b/ruby.c\n@@ -265,6 +265,18 @@ static VALUE git_rb_find_unique_abbrev(VALUE self, VALUE sha1, VALUE len)\n \treturn rb_str_new2(abbrev);\n }\n \n+static VALUE git_rb_read_sha1_file(VALUE self, VALUE sha1, VALUE type)\n+{\n+\tenum object_type g_type;\n+\tvoid *buffer;\n+\tunsigned long size;\n+\n+\tbuffer = read_sha1_file(str_to_sha1(sha1), &g_type, &size);\n+\tif (!buffer)\n+\t\treturn Qnil;\n+\treturn rb_ary_new3(2, rb_str_new(buffer, size), INT2FIX(g_type));\n+}\n+\n static void git_ruby_init(void)\n {\n \tVALUE mod;\n@@ -295,6 +307,7 @@ static void git_ruby_init(void)\n \trb_define_global_function(\"remote_get\", git_rb_remote_get, 1);\n \trb_define_global_function(\"transport_get\", git_rb_transport_get, 2);\n \trb_define_global_function(\"find_unique_abbrev\", git_rb_find_unique_abbrev, 2);\n+\trb_define_global_function(\"read_sha1_file\", git_rb_read_sha1_file, 1);\n \n \tgit_rb_object = rb_define_class_under(mod, \"Object\", rb_cData);\n \trb_define_singleton_method(git_rb_object, \"get\", git_rb_object_get, 1);\n-- \n1.8.4-fc\n"},{"id":"228408","messageId":"1380405849-13000-32-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 31/44] ruby: request-pull: use read_sha1_file()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:56Z","receivedAt":"2013-09-28T22:03:56Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n git-request-pull.rb | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nindex 5f40901..233981f 100755\n--- a/git-request-pull.rb\n+++ b/git-request-pull.rb\n@@ -165,7 +165,8 @@ for you to fetch changes up to %s:\n       $stderr.puts \"warn: appear to be at #{url}\"\n       $stderr.puts \"warn: Do you want to push it there, perhaps?\"\n     end\n-    run(%[git cat-file tag \"#{tag_name}\" | sed -n -e '1,/^$/d' -e '/^-----BEGIN PGP /q' -e p])\n+    buffer, _ = read_sha1_file(get_sha1(tag_name))\n+    puts buffer.scan(/(?:\\n\\n)(.+)(?:-----BEGIN PGP )?/m).first\n     puts\n   end\n \n-- \n1.8.4-fc\n"},{"id":"228409","messageId":"1380405849-13000-33-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 32/44] revision: add missing include","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:57Z","receivedAt":"2013-09-28T22:03:57Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Otherwise we might not have 'struct diff_options'.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n revision.h | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/revision.h b/revision.h\nindex 95859ba..4aa5c5a 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -5,6 +5,7 @@\n #include \"grep.h\"\n #include \"notes.h\"\n #include \"commit.h\"\n+#include \"diff.h\"\n \n #define SEEN\t\t(1u<<0)\n #define UNINTERESTING   (1u<<1)\n-- \n1.8.4-fc\n"},{"id":"228410","messageId":"1380405849-13000-34-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 33/44] shortlog: add missing declaration","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:58Z","receivedAt":"2013-09-28T22:03:58Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Otherwise we would have to include commit.h.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n shortlog.h | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/shortlog.h b/shortlog.h\nindex de4f86f..54bc07c 100644\n--- a/shortlog.h\n+++ b/shortlog.h\n@@ -19,6 +19,8 @@ struct shortlog {\n \tstruct string_list mailmap;\n };\n \n+struct commit;\n+\n void shortlog_init(struct shortlog *log);\n \n void shortlog_add_commit(struct shortlog *log, struct commit *commit);\n-- \n1.8.4-fc\n"},{"id":"228413","messageId":"1380405849-13000-35-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 34/44] shortlog: split builtin from common code","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:03:59Z","receivedAt":"2013-09-28T22:03:59Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    Somebody forgot to do this.\n\n Makefile           |   1 +\n builtin/shortlog.c | 184 +----------------------------------------------------\n shortlog.c         | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n shortlog.h         |   6 ++\n 4 files changed, 189 insertions(+), 183 deletions(-)\n create mode 100644 shortlog.c\n\ndiff --git a/Makefile b/Makefile\nindex 44b4cd5..709c087 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -880,6 +880,7 @@ LIB_OBJS += sha1-lookup.o\n LIB_OBJS += sha1_file.o\n LIB_OBJS += sha1_name.o\n LIB_OBJS += shallow.o\n+LIB_OBJS += shortlog.o\n LIB_OBJS += sideband.o\n LIB_OBJS += sigchain.o\n LIB_OBJS += strbuf.o\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex 1434f8f..f84c6f8 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -1,11 +1,8 @@\n #include \"builtin.h\"\n #include \"cache.h\"\n #include \"commit.h\"\n-#include \"diff.h\"\n #include \"string-list.h\"\n #include \"revision.h\"\n-#include \"utf8.h\"\n-#include \"mailmap.h\"\n #include \"shortlog.h\"\n #include \"parse-options.h\"\n \n@@ -14,81 +11,6 @@ static char const * const shortlog_usage[] = {\n \tNULL\n };\n \n-static int compare_by_number(const void *a1, const void *a2)\n-{\n-\tconst struct string_list_item *i1 = a1, *i2 = a2;\n-\tconst struct string_list *l1 = i1->util, *l2 = i2->util;\n-\n-\tif (l1->nr < l2->nr)\n-\t\treturn 1;\n-\telse if (l1->nr == l2->nr)\n-\t\treturn 0;\n-\telse\n-\t\treturn -1;\n-}\n-\n-static void insert_one_record(struct shortlog *log,\n-\t\t\t      const char *author,\n-\t\t\t      const char *oneline)\n-{\n-\tconst char *dot3 = log->common_repo_prefix;\n-\tchar *buffer, *p;\n-\tstruct string_list_item *item;\n-\tconst char *mailbuf, *namebuf;\n-\tsize_t namelen, maillen;\n-\tconst char *eol;\n-\tstruct strbuf subject = STRBUF_INIT;\n-\tstruct strbuf namemailbuf = STRBUF_INIT;\n-\tstruct ident_split ident;\n-\n-\tif (split_ident_line(&ident, author, strlen(author)))\n-\t\treturn;\n-\n-\tnamebuf = ident.name_begin;\n-\tmailbuf = ident.mail_begin;\n-\tnamelen = ident.name_end - ident.name_begin;\n-\tmaillen = ident.mail_end - ident.mail_begin;\n-\n-\tmap_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen);\n-\tstrbuf_add(&namemailbuf, namebuf, namelen);\n-\n-\tif (log->email)\n-\t\tstrbuf_addf(&namemailbuf, \" <%.*s>\", (int)maillen, mailbuf);\n-\n-\titem = string_list_insert(&log->list, namemailbuf.buf);\n-\tif (item->util == NULL)\n-\t\titem->util = xcalloc(1, sizeof(struct string_list));\n-\n-\t/* Skip any leading whitespace, including any blank lines. */\n-\twhile (*oneline && isspace(*oneline))\n-\t\toneline++;\n-\teol = strchr(oneline, '\\n');\n-\tif (!eol)\n-\t\teol = oneline + strlen(oneline);\n-\tif (!prefixcmp(oneline, \"[PATCH\")) {\n-\t\tchar *eob = strchr(oneline, ']');\n-\t\tif (eob && (!eol || eob < eol))\n-\t\t\toneline = eob + 1;\n-\t}\n-\twhile (*oneline && isspace(*oneline) && *oneline != '\\n')\n-\t\toneline++;\n-\tformat_subject(&subject, oneline, \" \");\n-\tbuffer = strbuf_detach(&subject, NULL);\n-\n-\tif (dot3) {\n-\t\tint dot3len = strlen(dot3);\n-\t\tif (dot3len > 5) {\n-\t\t\twhile ((p = strstr(buffer, dot3)) != NULL) {\n-\t\t\t\tint taillen = strlen(p) - dot3len;\n-\t\t\t\tmemcpy(p, \"/.../\", 5);\n-\t\t\t\tmemmove(p + 5, p + dot3len, taillen + 1);\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\tstring_list_append(item->util, buffer);\n-}\n-\n static void read_from_stdin(struct shortlog *log)\n {\n \tchar author[1024], oneline[1024];\n@@ -103,51 +25,10 @@ static void read_from_stdin(struct shortlog *log)\n \t\twhile (fgets(oneline, sizeof(oneline), stdin) &&\n \t\t       oneline[0] == '\\n')\n \t\t\t; /* discard blanks */\n-\t\tinsert_one_record(log, author + 8, oneline);\n+\t\tshortlog_insert_one_record(log, author + 8, oneline);\n \t}\n }\n \n-void shortlog_add_commit(struct shortlog *log, struct commit *commit)\n-{\n-\tconst char *author = NULL, *buffer;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tstruct strbuf ufbuf = STRBUF_INIT;\n-\n-\tpp_commit_easy(CMIT_FMT_RAW, commit, &buf);\n-\tbuffer = buf.buf;\n-\twhile (*buffer && *buffer != '\\n') {\n-\t\tconst char *eol = strchr(buffer, '\\n');\n-\n-\t\tif (eol == NULL)\n-\t\t\teol = buffer + strlen(buffer);\n-\t\telse\n-\t\t\teol++;\n-\n-\t\tif (!prefixcmp(buffer, \"author \"))\n-\t\t\tauthor = buffer + 7;\n-\t\tbuffer = eol;\n-\t}\n-\tif (!author)\n-\t\tdie(_(\"Missing author: %s\"),\n-\t\t    sha1_to_hex(commit->object.sha1));\n-\tif (log->user_format) {\n-\t\tstruct pretty_print_context ctx = {0};\n-\t\tctx.fmt = CMIT_FMT_USERFORMAT;\n-\t\tctx.abbrev = log->abbrev;\n-\t\tctx.subject = \"\";\n-\t\tctx.after_subject = \"\";\n-\t\tctx.date_mode = DATE_NORMAL;\n-\t\tctx.output_encoding = get_log_output_encoding();\n-\t\tpretty_print_commit(&ctx, commit, &ufbuf);\n-\t\tbuffer = ufbuf.buf;\n-\t} else if (*buffer) {\n-\t\tbuffer++;\n-\t}\n-\tinsert_one_record(log, author, !*buffer ? \"<none>\" : buffer);\n-\tstrbuf_release(&ufbuf);\n-\tstrbuf_release(&buf);\n-}\n-\n static void get_from_rev(struct rev_info *rev, struct shortlog *log)\n {\n \tstruct commit *commit;\n@@ -175,9 +56,6 @@ static int parse_uint(char const **arg, int comma, int defval)\n }\n \n static const char wrap_arg_usage[] = \"-w[<width>[,<indent1>[,<indent2>]]]\";\n-#define DEFAULT_WRAPLEN 76\n-#define DEFAULT_INDENT1 6\n-#define DEFAULT_INDENT2 9\n \n static int parse_wrap_args(const struct option *opt, const char *arg, int unset)\n {\n@@ -205,18 +83,6 @@ static int parse_wrap_args(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n-void shortlog_init(struct shortlog *log)\n-{\n-\tmemset(log, 0, sizeof(*log));\n-\n-\tread_mailmap(&log->mailmap, &log->common_repo_prefix);\n-\n-\tlog->list.strdup_strings = 1;\n-\tlog->wrap = DEFAULT_WRAPLEN;\n-\tlog->in1 = DEFAULT_INDENT1;\n-\tlog->in2 = DEFAULT_INDENT2;\n-}\n-\n int cmd_shortlog(int argc, const char **argv, const char *prefix)\n {\n \tstatic struct shortlog log;\n@@ -277,51 +143,3 @@ parse_done:\n \tshortlog_output(&log);\n \treturn 0;\n }\n-\n-static void add_wrapped_shortlog_msg(struct strbuf *sb, const char *s,\n-\t\t\t\t     const struct shortlog *log)\n-{\n-\tstrbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap);\n-\tstrbuf_addch(sb, '\\n');\n-}\n-\n-void shortlog_output(struct shortlog *log)\n-{\n-\tint i, j;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\n-\tif (log->sort_by_number)\n-\t\tqsort(log->list.items, log->list.nr, sizeof(struct string_list_item),\n-\t\t\tcompare_by_number);\n-\tfor (i = 0; i < log->list.nr; i++) {\n-\t\tstruct string_list *onelines = log->list.items[i].util;\n-\n-\t\tif (log->summary) {\n-\t\t\tprintf(\"%6d\\t%s\\n\", onelines->nr, log->list.items[i].string);\n-\t\t} else {\n-\t\t\tprintf(\"%s (%d):\\n\", log->list.items[i].string, onelines->nr);\n-\t\t\tfor (j = onelines->nr - 1; j >= 0; j--) {\n-\t\t\t\tconst char *msg = onelines->items[j].string;\n-\n-\t\t\t\tif (log->wrap_lines) {\n-\t\t\t\t\tstrbuf_reset(&sb);\n-\t\t\t\t\tadd_wrapped_shortlog_msg(&sb, msg, log);\n-\t\t\t\t\tfwrite(sb.buf, sb.len, 1, stdout);\n-\t\t\t\t}\n-\t\t\t\telse\n-\t\t\t\t\tprintf(\"      %s\\n\", msg);\n-\t\t\t}\n-\t\t\tputchar('\\n');\n-\t\t}\n-\n-\t\tonelines->strdup_strings = 1;\n-\t\tstring_list_clear(onelines, 0);\n-\t\tfree(onelines);\n-\t\tlog->list.items[i].util = NULL;\n-\t}\n-\n-\tstrbuf_release(&sb);\n-\tlog->list.strdup_strings = 1;\n-\tstring_list_clear(&log->list, 1);\n-\tclear_mailmap(&log->mailmap);\n-}\ndiff --git a/shortlog.c b/shortlog.c\nnew file mode 100644\nindex 0000000..89a839f\n--- /dev/null\n+++ b/shortlog.c\n@@ -0,0 +1,181 @@\n+#include \"cache.h\"\n+#include \"shortlog.h\"\n+#include \"commit.h\"\n+#include \"mailmap.h\"\n+#include \"utf8.h\"\n+\n+void shortlog_init(struct shortlog *log)\n+{\n+\tmemset(log, 0, sizeof(*log));\n+\n+\tread_mailmap(&log->mailmap, &log->common_repo_prefix);\n+\n+\tlog->list.strdup_strings = 1;\n+\tlog->wrap = DEFAULT_WRAPLEN;\n+\tlog->in1 = DEFAULT_INDENT1;\n+\tlog->in2 = DEFAULT_INDENT2;\n+}\n+\n+void shortlog_insert_one_record(struct shortlog *log,\n+\t\tconst char *author,\n+\t\tconst char *oneline)\n+{\n+\tconst char *dot3 = log->common_repo_prefix;\n+\tchar *buffer, *p;\n+\tstruct string_list_item *item;\n+\tconst char *mailbuf, *namebuf;\n+\tsize_t namelen, maillen;\n+\tconst char *eol;\n+\tstruct strbuf subject = STRBUF_INIT;\n+\tstruct strbuf namemailbuf = STRBUF_INIT;\n+\tstruct ident_split ident;\n+\n+\tif (split_ident_line(&ident, author, strlen(author)))\n+\t\treturn;\n+\n+\tnamebuf = ident.name_begin;\n+\tmailbuf = ident.mail_begin;\n+\tnamelen = ident.name_end - ident.name_begin;\n+\tmaillen = ident.mail_end - ident.mail_begin;\n+\n+\tmap_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen);\n+\tstrbuf_add(&namemailbuf, namebuf, namelen);\n+\n+\tif (log->email)\n+\t\tstrbuf_addf(&namemailbuf, \" <%.*s>\", (int)maillen, mailbuf);\n+\n+\titem = string_list_insert(&log->list, namemailbuf.buf);\n+\tif (item->util == NULL)\n+\t\titem->util = xcalloc(1, sizeof(struct string_list));\n+\n+\t/* Skip any leading whitespace, including any blank lines. */\n+\twhile (*oneline && isspace(*oneline))\n+\t\toneline++;\n+\teol = strchr(oneline, '\\n');\n+\tif (!eol)\n+\t\teol = oneline + strlen(oneline);\n+\tif (!prefixcmp(oneline, \"[PATCH\")) {\n+\t\tchar *eob = strchr(oneline, ']');\n+\t\tif (eob && (!eol || eob < eol))\n+\t\t\toneline = eob + 1;\n+\t}\n+\twhile (*oneline && isspace(*oneline) && *oneline != '\\n')\n+\t\toneline++;\n+\tformat_subject(&subject, oneline, \" \");\n+\tbuffer = strbuf_detach(&subject, NULL);\n+\n+\tif (dot3) {\n+\t\tint dot3len = strlen(dot3);\n+\t\tif (dot3len > 5) {\n+\t\t\twhile ((p = strstr(buffer, dot3)) != NULL) {\n+\t\t\t\tint taillen = strlen(p) - dot3len;\n+\t\t\t\tmemcpy(p, \"/.../\", 5);\n+\t\t\t\tmemmove(p + 5, p + dot3len, taillen + 1);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tstring_list_append(item->util, buffer);\n+}\n+\n+void shortlog_add_commit(struct shortlog *log, struct commit *commit)\n+{\n+\tconst char *author = NULL, *buffer;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct strbuf ufbuf = STRBUF_INIT;\n+\n+\tpp_commit_easy(CMIT_FMT_RAW, commit, &buf);\n+\tbuffer = buf.buf;\n+\twhile (*buffer && *buffer != '\\n') {\n+\t\tconst char *eol = strchr(buffer, '\\n');\n+\n+\t\tif (eol == NULL)\n+\t\t\teol = buffer + strlen(buffer);\n+\t\telse\n+\t\t\teol++;\n+\n+\t\tif (!prefixcmp(buffer, \"author \"))\n+\t\t\tauthor = buffer + 7;\n+\t\tbuffer = eol;\n+\t}\n+\tif (!author)\n+\t\tdie(_(\"Missing author: %s\"),\n+\t\t    sha1_to_hex(commit->object.sha1));\n+\tif (log->user_format) {\n+\t\tstruct pretty_print_context ctx = {0};\n+\t\tctx.fmt = CMIT_FMT_USERFORMAT;\n+\t\tctx.abbrev = log->abbrev;\n+\t\tctx.subject = \"\";\n+\t\tctx.after_subject = \"\";\n+\t\tctx.date_mode = DATE_NORMAL;\n+\t\tctx.output_encoding = get_log_output_encoding();\n+\t\tpretty_print_commit(&ctx, commit, &ufbuf);\n+\t\tbuffer = ufbuf.buf;\n+\t} else if (*buffer) {\n+\t\tbuffer++;\n+\t}\n+\tshortlog_insert_one_record(log, author, !*buffer ? \"<none>\" : buffer);\n+\tstrbuf_release(&ufbuf);\n+\tstrbuf_release(&buf);\n+}\n+\n+static int compare_by_number(const void *a1, const void *a2)\n+{\n+\tconst struct string_list_item *i1 = a1, *i2 = a2;\n+\tconst struct string_list *l1 = i1->util, *l2 = i2->util;\n+\n+\tif (l1->nr < l2->nr)\n+\t\treturn 1;\n+\telse if (l1->nr == l2->nr)\n+\t\treturn 0;\n+\telse\n+\t\treturn -1;\n+}\n+\n+static void add_wrapped_shortlog_msg(struct strbuf *sb, const char *s,\n+\t\t\t\t     const struct shortlog *log)\n+{\n+\tstrbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap);\n+\tstrbuf_addch(sb, '\\n');\n+}\n+\n+void shortlog_output(struct shortlog *log)\n+{\n+\tint i, j;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\tif (log->sort_by_number)\n+\t\tqsort(log->list.items, log->list.nr, sizeof(struct string_list_item),\n+\t\t\tcompare_by_number);\n+\tfor (i = 0; i < log->list.nr; i++) {\n+\t\tstruct string_list *onelines = log->list.items[i].util;\n+\n+\t\tif (log->summary) {\n+\t\t\tprintf(\"%6d\\t%s\\n\", onelines->nr, log->list.items[i].string);\n+\t\t} else {\n+\t\t\tprintf(\"%s (%d):\\n\", log->list.items[i].string, onelines->nr);\n+\t\t\tfor (j = onelines->nr - 1; j >= 0; j--) {\n+\t\t\t\tconst char *msg = onelines->items[j].string;\n+\n+\t\t\t\tif (log->wrap_lines) {\n+\t\t\t\t\tstrbuf_reset(&sb);\n+\t\t\t\t\tadd_wrapped_shortlog_msg(&sb, msg, log);\n+\t\t\t\t\tfwrite(sb.buf, sb.len, 1, stdout);\n+\t\t\t\t}\n+\t\t\t\telse\n+\t\t\t\t\tprintf(\"      %s\\n\", msg);\n+\t\t\t}\n+\t\t\tputchar('\\n');\n+\t\t}\n+\n+\t\tonelines->strdup_strings = 1;\n+\t\tstring_list_clear(onelines, 0);\n+\t\tfree(onelines);\n+\t\tlog->list.items[i].util = NULL;\n+\t}\n+\n+\tstrbuf_release(&sb);\n+\tlog->list.strdup_strings = 1;\n+\tstring_list_clear(&log->list, 1);\n+\tclear_mailmap(&log->mailmap);\n+}\ndiff --git a/shortlog.h b/shortlog.h\nindex 54bc07c..cf14d8b 100644\n--- a/shortlog.h\n+++ b/shortlog.h\n@@ -3,6 +3,10 @@\n \n #include \"string-list.h\"\n \n+#define DEFAULT_WRAPLEN 76\n+#define DEFAULT_INDENT1 6\n+#define DEFAULT_INDENT2 9\n+\n struct shortlog {\n \tstruct string_list list;\n \tint summary;\n@@ -25,6 +29,8 @@ void shortlog_init(struct shortlog *log);\n \n void shortlog_add_commit(struct shortlog *log, struct commit *commit);\n \n+void shortlog_insert_one_record(struct shortlog *log, const char *author, const char *oneline);\n+\n void shortlog_output(struct shortlog *log);\n \n #endif\n-- \n1.8.4-fc\n"},{"id":"228412","messageId":"1380405849-13000-36-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 35/44] ruby: add RevInfo and DiffOptions","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:04:00Z","receivedAt":"2013-09-28T22:04:00Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    This time we use an initializer and alloc function, so we could do:\n    \n     ri = Git::RevInfo.new()\n     ri.setup(args)\n    \n    Or:\n    \n     ri = Git::RevInfo.setup(args)\n    \n    The effect is the same.\n    \n    Also, we use the extremely useful 'method_missing' method, which allows us to\n    do the binding at run-time, saving us precious finger typing time and code,\n    with price of a bit of performance loss, which is not that important in a\n    script like this.\n    \n    We could do something fancier like saving the Ruby internal identifiers in an\n    array to map them to a C enum, or we could even generate the missing methods at\n    run-time, but for now let's stick to the simpler option and just make it work\n    with the less possible code.\n\n ruby.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 165 insertions(+)\n\ndiff --git a/ruby.c b/ruby.c\nindex 360515f..d9ee985 100644\n--- a/ruby.c\n+++ b/ruby.c\n@@ -5,6 +5,8 @@\n #include \"commit.h\"\n #include \"remote.h\"\n #include \"transport.h\"\n+#include \"revision.h\"\n+#include \"diff.h\"\n \n #undef NORETURN\n #undef PATH_SEP\n@@ -17,6 +19,8 @@ static VALUE git_rb_commit_list;\n static VALUE git_rb_remote;\n static VALUE git_rb_transport;\n static VALUE git_rb_ref;\n+static VALUE git_rb_rev_info;\n+static VALUE git_rb_diff_opt;\n \n static inline VALUE sha1_to_str(const unsigned char *sha1)\n {\n@@ -277,6 +281,107 @@ static VALUE git_rb_read_sha1_file(VALUE self, VALUE sha1, VALUE type)\n \treturn rb_ary_new3(2, rb_str_new(buffer, size), INT2FIX(g_type));\n }\n \n+static VALUE git_rb_rev_info_alloc(VALUE class)\n+{\n+\tstruct rev_info *revs;\n+\treturn Data_Make_Struct(class, struct rev_info, NULL, free, revs);\n+}\n+\n+static VALUE git_rb_rev_info_init(VALUE self, VALUE prefix)\n+{\n+\tstruct rev_info *revs;\n+\tData_Get_Struct(self, struct rev_info, revs);\n+\tinit_revisions(revs, str_to_cstr(prefix));\n+\treturn self;\n+}\n+\n+static VALUE git_rb_rev_info_setup(VALUE self, VALUE args, VALUE opts)\n+{\n+\tstruct rev_info *revs;\n+\tconst char *argv[RARRAY_LEN(args) + 2];\n+\tint i, r;\n+\n+\targv[0] = \"\";\n+\tData_Get_Struct(self, struct rev_info, revs);\n+\tfor (i = 0; i < RARRAY_LEN(args); i++)\n+\t\targv[i + 1] = RSTRING_PTR(RARRAY_PTR(args)[i]);\n+\targv[i + 1] = NULL;\n+\tr = setup_revisions(RARRAY_LEN(args) + 1, argv, revs, NULL);\n+\treturn INT2FIX(r - 1);\n+}\n+\n+static VALUE git_rb_rev_info_single_setup(VALUE class, VALUE prefix, VALUE args, VALUE opts)\n+{\n+\tstruct rev_info *revs;\n+\tVALUE self;\n+\tself = Data_Make_Struct(class, struct rev_info, NULL, free, revs);\n+\tinit_revisions(revs, str_to_cstr(prefix));\n+\tgit_rb_rev_info_setup(self, args, opts);\n+\treturn self;\n+}\n+\n+static VALUE git_rb_rev_info_each_revision(VALUE self, VALUE args, VALUE opts)\n+{\n+\tstruct commit *commit;\n+\tstruct rev_info *revs;\n+\n+\tData_Get_Struct(self, struct rev_info, revs);\n+\tif (prepare_revision_walk(revs))\n+\t\treturn Qnil;\n+\twhile ((commit = get_revision(revs))) {\n+\t\tVALUE c;\n+\t\tc = Data_Wrap_Struct(git_rb_commit, NULL, NULL, commit);\n+\t\trb_yield(c);\n+\t}\n+\treturn Qnil;\n+}\n+\n+static VALUE git_rb_rev_info_diffopt(VALUE self)\n+{\n+\tstruct rev_info *revs;\n+\n+\tData_Get_Struct(self, struct rev_info, revs);\n+\treturn Data_Wrap_Struct(git_rb_diff_opt, NULL, NULL, &revs->diffopt);\n+}\n+\n+static VALUE git_rb_diff_opt_new(VALUE class)\n+{\n+\tstruct diff_options *opt;\n+\tVALUE self;\n+\tself = Data_Make_Struct(class, struct diff_options, NULL, free, opt);\n+\n+\tdiff_setup(opt);\n+\n+\treturn self;\n+}\n+\n+static VALUE git_rb_diff_opt_method_missing(int argc, VALUE *argv, VALUE self)\n+{\n+\tstruct diff_options *opt;\n+\tID id;\n+\n+\tid = rb_to_id(argv[0]);\n+\tData_Get_Struct(self, struct diff_options, opt);\n+\n+\tif (id == rb_intern(\"stat_width=\"))\n+\t\topt->stat_width = NUM2INT(argv[1]);\n+\telse if (id == rb_intern(\"stat_graph_width=\"))\n+\t\topt->stat_graph_width = NUM2INT(argv[1]);\n+\telse if (id == rb_intern(\"output_format=\"))\n+\t\topt->output_format = NUM2INT(argv[1]);\n+\telse if (id == rb_intern(\"detect_rename=\"))\n+\t\topt->detect_rename = NUM2INT(argv[1]);\n+\telse if (id == rb_intern(\"flags=\"))\n+\t\topt->flags = NUM2INT(argv[1]);\n+\telse if (id == rb_intern(\"output_format\"))\n+\t\treturn INT2FIX(opt->output_format);\n+\telse if (id == rb_intern(\"flags\"))\n+\t\treturn INT2FIX(opt->flags);\n+\telse\n+\t\treturn rb_call_super(argc, argv);\n+\treturn Qnil;\n+}\n+\n static void git_ruby_init(void)\n {\n \tVALUE mod;\n@@ -296,6 +401,53 @@ static void git_ruby_init(void)\n \n \trb_define_global_const(\"DEFAULT_ABBREV\", INT2FIX(DEFAULT_ABBREV));\n \n+\trb_define_global_const(\"DIFF_FORMAT_RAW\", INT2FIX(DIFF_FORMAT_RAW));\n+\trb_define_global_const(\"DIFF_FORMAT_DIFFSTAT\", INT2FIX(DIFF_FORMAT_DIFFSTAT));\n+\trb_define_global_const(\"DIFF_FORMAT_NUMSTAT\", INT2FIX(DIFF_FORMAT_NUMSTAT));\n+\trb_define_global_const(\"DIFF_FORMAT_SUMMARY\", INT2FIX(DIFF_FORMAT_SUMMARY));\n+\trb_define_global_const(\"DIFF_FORMAT_PATCH\", INT2FIX(DIFF_FORMAT_PATCH));\n+\trb_define_global_const(\"DIFF_FORMAT_SHORTSTAT\", INT2FIX(DIFF_FORMAT_SHORTSTAT));\n+\trb_define_global_const(\"DIFF_FORMAT_DIRSTAT\", INT2FIX(DIFF_FORMAT_DIRSTAT));\n+\trb_define_global_const(\"DIFF_FORMAT_NAME\", INT2FIX(DIFF_FORMAT_NAME));\n+\trb_define_global_const(\"DIFF_FORMAT_NAME_STATUS\", INT2FIX(DIFF_FORMAT_NAME_STATUS));\n+\trb_define_global_const(\"DIFF_FORMAT_CHECKDIFF\", INT2FIX(DIFF_FORMAT_CHECKDIFF));\n+\trb_define_global_const(\"DIFF_FORMAT_NO_OUTPUT\", INT2FIX(DIFF_FORMAT_NO_OUTPUT));\n+\trb_define_global_const(\"DIFF_FORMAT_CALLBACK\", INT2FIX(DIFF_FORMAT_CALLBACK));\n+\n+\trb_define_global_const(\"DIFF_DETECT_RENAME\", INT2FIX(DIFF_DETECT_RENAME));\n+\trb_define_global_const(\"DIFF_DETECT_COPY\", INT2FIX(DIFF_DETECT_COPY));\n+\n+\trb_define_global_const(\"DIFF_OPT_RECURSIVE\", INT2FIX(DIFF_OPT_RECURSIVE));\n+\trb_define_global_const(\"DIFF_OPT_TREE_IN_RECURSIVE\", INT2FIX(DIFF_OPT_TREE_IN_RECURSIVE));\n+\trb_define_global_const(\"DIFF_OPT_BINARY\", INT2FIX(DIFF_OPT_BINARY));\n+\trb_define_global_const(\"DIFF_OPT_TEXT\", INT2FIX(DIFF_OPT_TEXT));\n+\trb_define_global_const(\"DIFF_OPT_FULL_INDEX\", INT2FIX(DIFF_OPT_FULL_INDEX));\n+\trb_define_global_const(\"DIFF_OPT_SILENT_ON_REMOVE\", INT2FIX(DIFF_OPT_SILENT_ON_REMOVE));\n+\trb_define_global_const(\"DIFF_OPT_FIND_COPIES_HARDER\", INT2FIX(DIFF_OPT_FIND_COPIES_HARDER));\n+\trb_define_global_const(\"DIFF_OPT_FOLLOW_RENAMES\", INT2FIX(DIFF_OPT_FOLLOW_RENAMES));\n+\trb_define_global_const(\"DIFF_OPT_RENAME_EMPTY\", INT2FIX(DIFF_OPT_RENAME_EMPTY));\n+\trb_define_global_const(\"DIFF_OPT_HAS_CHANGES\", INT2FIX(DIFF_OPT_HAS_CHANGES));\n+\trb_define_global_const(\"DIFF_OPT_QUICK\", INT2FIX(DIFF_OPT_QUICK));\n+\trb_define_global_const(\"DIFF_OPT_NO_INDEX\", INT2FIX(DIFF_OPT_NO_INDEX));\n+\trb_define_global_const(\"DIFF_OPT_ALLOW_EXTERNAL\", INT2FIX(DIFF_OPT_ALLOW_EXTERNAL));\n+\trb_define_global_const(\"DIFF_OPT_EXIT_WITH_STATUS\", INT2FIX(DIFF_OPT_EXIT_WITH_STATUS));\n+\trb_define_global_const(\"DIFF_OPT_REVERSE_DIFF\", INT2FIX(DIFF_OPT_REVERSE_DIFF));\n+\trb_define_global_const(\"DIFF_OPT_CHECK_FAILED\", INT2FIX(DIFF_OPT_CHECK_FAILED));\n+\trb_define_global_const(\"DIFF_OPT_RELATIVE_NAME\", INT2FIX(DIFF_OPT_RELATIVE_NAME));\n+\trb_define_global_const(\"DIFF_OPT_IGNORE_SUBMODULES\", INT2FIX(DIFF_OPT_IGNORE_SUBMODULES));\n+\trb_define_global_const(\"DIFF_OPT_DIRSTAT_CUMULATIVE\", INT2FIX(DIFF_OPT_DIRSTAT_CUMULATIVE));\n+\trb_define_global_const(\"DIFF_OPT_DIRSTAT_BY_FILE\", INT2FIX(DIFF_OPT_DIRSTAT_BY_FILE));\n+\trb_define_global_const(\"DIFF_OPT_ALLOW_TEXTCONV\", INT2FIX(DIFF_OPT_ALLOW_TEXTCONV));\n+\trb_define_global_const(\"DIFF_OPT_DIFF_FROM_CONTENTS\", INT2FIX(DIFF_OPT_DIFF_FROM_CONTENTS));\n+\trb_define_global_const(\"DIFF_OPT_SUBMODULE_LOG\", INT2FIX(DIFF_OPT_SUBMODULE_LOG));\n+\trb_define_global_const(\"DIFF_OPT_DIRTY_SUBMODULES\", INT2FIX(DIFF_OPT_DIRTY_SUBMODULES));\n+\trb_define_global_const(\"DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES\", INT2FIX(DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES));\n+\trb_define_global_const(\"DIFF_OPT_IGNORE_DIRTY_SUBMODULES\", INT2FIX(DIFF_OPT_IGNORE_DIRTY_SUBMODULES));\n+\trb_define_global_const(\"DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG\", INT2FIX(DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG));\n+\trb_define_global_const(\"DIFF_OPT_DIRSTAT_BY_LINE\", INT2FIX(DIFF_OPT_DIRSTAT_BY_LINE));\n+\trb_define_global_const(\"DIFF_OPT_FUNCCONTEXT\", INT2FIX(DIFF_OPT_FUNCCONTEXT));\n+\trb_define_global_const(\"DIFF_OPT_PICKAXE_IGNORE_CASE\", INT2FIX(DIFF_OPT_PICKAXE_IGNORE_CASE));\n+\n \trb_define_global_function(\"setup_git_directory\", git_rb_setup_git_directory, 0);\n \trb_define_global_function(\"for_each_ref\", git_rb_for_each_ref, 0);\n \trb_define_global_function(\"dwim_ref\", git_rb_dwim_ref, 1);\n@@ -334,6 +486,19 @@ static void git_ruby_init(void)\n \trb_define_method(git_rb_ref, \"each\", git_rb_ref_each, 0);\n \trb_define_method(git_rb_ref, \"name\", git_rb_ref_name, 0);\n \trb_define_method(git_rb_ref, \"old_sha1\", git_rb_ref_old_sha1, 0);\n+\n+\tgit_rb_rev_info = rb_define_class_under(mod, \"RevInfo\", rb_cData);\n+\trb_include_module(git_rb_rev_info, rb_mEnumerable);\n+\trb_define_alloc_func(git_rb_rev_info, git_rb_rev_info_alloc);\n+\trb_define_method(git_rb_rev_info, \"initialize\", git_rb_rev_info_init, 1);\n+\trb_define_singleton_method(git_rb_rev_info, \"setup\", git_rb_rev_info_single_setup, 3);\n+\trb_define_method(git_rb_rev_info, \"setup\", git_rb_rev_info_setup, 2);\n+\trb_define_method(git_rb_rev_info, \"each\", git_rb_rev_info_each_revision, 0);\n+\trb_define_method(git_rb_rev_info, \"diffopt\", git_rb_rev_info_diffopt, 0);\n+\n+\tgit_rb_diff_opt = rb_define_class_under(mod, \"DiffOptions\", rb_cData);\n+\trb_define_singleton_method(git_rb_diff_opt, \"new\", git_rb_diff_opt_new, 0);\n+\trb_define_method(git_rb_diff_opt, \"method_missing\", git_rb_diff_opt_method_missing, -1);\n }\n \n static int run_ruby_command(const char *cmd, int argc, const char **argv)\n-- \n1.8.4-fc\n"},{"id":"228411","messageId":"1380405849-13000-37-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 36/44] ruby: bind shortlog()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:04:01Z","receivedAt":"2013-09-28T22:04:01Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    There's no need to bind all the shortlog functions, we can easily pass all the\n    commits we want in Ruby.\n\n ruby.c | 17 +++++++++++++++++\n 1 file changed, 17 insertions(+)\n\ndiff --git a/ruby.c b/ruby.c\nindex d9ee985..d59aafd 100644\n--- a/ruby.c\n+++ b/ruby.c\n@@ -7,6 +7,7 @@\n #include \"transport.h\"\n #include \"revision.h\"\n #include \"diff.h\"\n+#include \"shortlog.h\"\n \n #undef NORETURN\n #undef PATH_SEP\n@@ -344,6 +345,21 @@ static VALUE git_rb_rev_info_diffopt(VALUE self)\n \treturn Data_Wrap_Struct(git_rb_diff_opt, NULL, NULL, &revs->diffopt);\n }\n \n+static VALUE git_rb_shortlog(VALUE self, VALUE commits)\n+{\n+\tstruct shortlog log;\n+\tint i;\n+\n+\tshortlog_init(&log);\n+\tfor (i = 0; i < RARRAY_LEN(commits); i++) {\n+\t\tstruct commit *commit;\n+\t\tData_Get_Struct(rb_ary_entry(commits, i), struct commit, commit);\n+\t\tshortlog_add_commit(&log, commit);\n+\t}\n+\tshortlog_output(&log);\n+\treturn Qnil;\n+}\n+\n static VALUE git_rb_diff_opt_new(VALUE class)\n {\n \tstruct diff_options *opt;\n@@ -460,6 +476,7 @@ static void git_ruby_init(void)\n \trb_define_global_function(\"transport_get\", git_rb_transport_get, 2);\n \trb_define_global_function(\"find_unique_abbrev\", git_rb_find_unique_abbrev, 2);\n \trb_define_global_function(\"read_sha1_file\", git_rb_read_sha1_file, 1);\n+\trb_define_global_function(\"shortlog\", git_rb_shortlog, 1);\n \n \tgit_rb_object = rb_define_class_under(mod, \"Object\", rb_cData);\n \trb_define_singleton_method(git_rb_object, \"get\", git_rb_object_get, 1);\n-- \n1.8.4-fc\n"},{"id":"228414","messageId":"1380405849-13000-38-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 37/44] ruby: request-pull: use shortlog()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:04:02Z","receivedAt":"2013-09-28T22:04:02Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n git-request-pull.rb | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nindex 233981f..0db64c3 100755\n--- a/git-request-pull.rb\n+++ b/git-request-pull.rb\n@@ -79,6 +79,11 @@ def parse_buffer(buffer)\n   return [summary, date]\n end\n \n+def show_shortlog(base, head)\n+  rev = Git::RevInfo.setup(nil, ['^' + base, head], nil)\n+  shortlog(rev.to_a)\n+end\n+\n until ARGV.empty?\n   case ARGV.first\n   when '-p'\n@@ -174,7 +179,7 @@ for you to fetch changes up to %s:\n     puts \"----------------------------------------------------------------\"\n   end\n \n-  run(%[git shortlog ^#{base} #{head}])\n+  show_shortlog(base, head)\n   run(%[git diff -M --stat --summary #{patch} ^#{merge_base_commit} #{head}])\n \n   if ! ref\n-- \n1.8.4-fc\n"},{"id":"228415","messageId":"1380405849-13000-39-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 38/44] ruby: bind diff_tree_sha1()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:04:03Z","receivedAt":"2013-09-28T22:04:03Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n ruby.c | 12 ++++++++++++\n 1 file changed, 12 insertions(+)\n\ndiff --git a/ruby.c b/ruby.c\nindex d59aafd..b491754 100644\n--- a/ruby.c\n+++ b/ruby.c\n@@ -360,6 +360,17 @@ static VALUE git_rb_shortlog(VALUE self, VALUE commits)\n \treturn Qnil;\n }\n \n+static VALUE git_rb_diff_tree_sha1(VALUE self, VALUE old, VALUE new, VALUE base, VALUE opt)\n+{\n+\tstruct diff_options *g_opt;\n+\tint r;\n+\n+\tData_Get_Struct(opt, struct diff_options, g_opt);\n+\n+\tr = diff_tree_sha1(str_to_sha1(old), str_to_sha1(new), str_to_cstr(base), g_opt);\n+\treturn INT2FIX(r);\n+}\n+\n static VALUE git_rb_diff_opt_new(VALUE class)\n {\n \tstruct diff_options *opt;\n@@ -477,6 +488,7 @@ static void git_ruby_init(void)\n \trb_define_global_function(\"find_unique_abbrev\", git_rb_find_unique_abbrev, 2);\n \trb_define_global_function(\"read_sha1_file\", git_rb_read_sha1_file, 1);\n \trb_define_global_function(\"shortlog\", git_rb_shortlog, 1);\n+\trb_define_global_function(\"diff_tree_sha1\", git_rb_diff_tree_sha1, 4);\n \n \tgit_rb_object = rb_define_class_under(mod, \"Object\", rb_cData);\n \trb_define_singleton_method(git_rb_object, \"get\", git_rb_object_get, 1);\n-- \n1.8.4-fc\n"},{"id":"228416","messageId":"1380405849-13000-40-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 39/44] ruby: bind log_tree_diff_flush()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:04:04Z","receivedAt":"2013-09-28T22:04:04Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n ruby.c | 12 ++++++++++++\n 1 file changed, 12 insertions(+)\n\ndiff --git a/ruby.c b/ruby.c\nindex b491754..71d5cb6 100644\n--- a/ruby.c\n+++ b/ruby.c\n@@ -8,6 +8,7 @@\n #include \"revision.h\"\n #include \"diff.h\"\n #include \"shortlog.h\"\n+#include \"log-tree.h\"\n \n #undef NORETURN\n #undef PATH_SEP\n@@ -371,6 +372,16 @@ static VALUE git_rb_diff_tree_sha1(VALUE self, VALUE old, VALUE new, VALUE base,\n \treturn INT2FIX(r);\n }\n \n+static VALUE git_rb_log_tree_diff_flush(VALUE self, VALUE revs)\n+{\n+\tstruct rev_info *g_revs;\n+\tint r;\n+\n+\tData_Get_Struct(revs, struct rev_info, g_revs);\n+\tr = log_tree_diff_flush(g_revs);\n+\treturn INT2FIX(r);\n+}\n+\n static VALUE git_rb_diff_opt_new(VALUE class)\n {\n \tstruct diff_options *opt;\n@@ -489,6 +500,7 @@ static void git_ruby_init(void)\n \trb_define_global_function(\"read_sha1_file\", git_rb_read_sha1_file, 1);\n \trb_define_global_function(\"shortlog\", git_rb_shortlog, 1);\n \trb_define_global_function(\"diff_tree_sha1\", git_rb_diff_tree_sha1, 4);\n+\trb_define_global_function(\"log_tree_diff_flush\", git_rb_log_tree_diff_flush, 1);\n \n \tgit_rb_object = rb_define_class_under(mod, \"Object\", rb_cData);\n \trb_define_singleton_method(git_rb_object, \"get\", git_rb_object_get, 1);\n-- \n1.8.4-fc\n"},{"id":"228417","messageId":"1380405849-13000-41-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 40/44] ruby: request-pull: use native diff_tree stuff","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:04:05Z","receivedAt":"2013-09-28T22:04:05Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    We finally got rid of all the system() calls, and thus all the forks.\n\n git-request-pull.rb | 17 +++++++++++++++--\n 1 file changed, 15 insertions(+), 2 deletions(-)\n\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nindex 0db64c3..e884d0d 100755\n--- a/git-request-pull.rb\n+++ b/git-request-pull.rb\n@@ -4,7 +4,7 @@ require 'date'\n \n ENV['GIT_PAGER'] =\n \n-patch = ''\n+patch = nil\n \n def usage\n   puts <<EOF\n@@ -84,6 +84,19 @@ def show_shortlog(base, head)\n   shortlog(rev.to_a)\n end\n \n+def show_diff(patch, base, head)\n+  rev = Git::RevInfo.setup(nil, ['^' + sha1_to_hex(base), sha1_to_hex(head)], nil)\n+  rev.diffopt.stat_width = -1\n+  rev.diffopt.stat_graph_width = -1\n+  rev.diffopt.output_format = patch ? DIFF_FORMAT_PATCH : DIFF_FORMAT_DIFFSTAT\n+  rev.diffopt.output_format |= DIFF_FORMAT_SUMMARY\n+  rev.diffopt.detect_rename = DIFF_DETECT_RENAME\n+  rev.diffopt.flags |= DIFF_OPT_RECURSIVE\n+\n+  diff_tree_sha1(base, head, \"\", rev.diffopt)\n+  log_tree_diff_flush(rev)\n+end\n+\n until ARGV.empty?\n   case ARGV.first\n   when '-p'\n@@ -180,7 +193,7 @@ for you to fetch changes up to %s:\n   end\n \n   show_shortlog(base, head)\n-  run(%[git diff -M --stat --summary #{patch} ^#{merge_base_commit} #{head}])\n+  show_diff(patch, merge_base_id, head_id)\n \n   if ! ref\n     $stderr.puts \"warn: No branch of #{url} is at:\"\n-- \n1.8.4-fc\n"},{"id":"228418","messageId":"1380405849-13000-42-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 41/44] ruby: request-pull: remove rescue block","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:04:06Z","receivedAt":"2013-09-28T22:04:06Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"We are not calling any git commands any more.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n git-request-pull.rb | 61 ++++++++++++++++++++++++-----------------------------\n 1 file changed, 27 insertions(+), 34 deletions(-)\n\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nindex e884d0d..fde8d1a 100755\n--- a/git-request-pull.rb\n+++ b/git-request-pull.rb\n@@ -115,7 +115,6 @@ end\n base = ARGV[0]\n url = ARGV[1]\n head = ARGV[2] || 'HEAD'\n-status = 0\n branch_name = branch_desc = nil\n \n usage unless base or url\n@@ -154,16 +153,15 @@ url = remote.url.first\n merge_base_summary, merge_base_date = parse_buffer(merge_base_commit.buffer)\n head_summary, head_date = parse_buffer(head_commit.buffer)\n \n-begin\n-  puts \"The following changes since commit %s:\n+puts \"The following changes since commit %s:\n \n   %s (%s)\n \n are available in the git repository at:\n \n \" % [merge_base_commit, merge_base_summary, merge_base_date]\n-  puts \"  #{url}\" + (ref ? \" #{ref}\" : \"\")\n-  puts \"\n+puts \"  #{url}\" + (ref ? \" #{ref}\" : \"\")\n+puts \"\n for you to fetch changes up to %s:\n \n   %s (%s)\n@@ -171,38 +169,33 @@ for you to fetch changes up to %s:\n ----------------------------------------------------------------\n \" % [head_commit, head_summary, head_date]\n \n-  if branch_name\n-    puts \"(from the branch description for #{branch_name} local branch)\"\n-    puts\n-    puts branch_desc\n-  end\n+if branch_name\n+  puts \"(from the branch description for #{branch_name} local branch)\"\n+  puts\n+  puts branch_desc\n+end\n \n-  if tag_name\n-    if ref != \"tags/#{tag_name}\"\n-      $stderr.puts \"warn: You locally have #{tag_name} but it does not (yet)\"\n-      $stderr.puts \"warn: appear to be at #{url}\"\n-      $stderr.puts \"warn: Do you want to push it there, perhaps?\"\n-    end\n-    buffer, _ = read_sha1_file(get_sha1(tag_name))\n-    puts buffer.scan(/(?:\\n\\n)(.+)(?:-----BEGIN PGP )?/m).first\n-    puts\n+if tag_name\n+  if ref != \"tags/#{tag_name}\"\n+    $stderr.puts \"warn: You locally have #{tag_name} but it does not (yet)\"\n+    $stderr.puts \"warn: appear to be at #{url}\"\n+    $stderr.puts \"warn: Do you want to push it there, perhaps?\"\n   end\n+  buffer, _ = read_sha1_file(get_sha1(tag_name))\n+  puts buffer.scan(/(?:\\n\\n)(.+)(?:-----BEGIN PGP )?/m).first\n+  puts\n+end\n \n-  if branch_name || tag_name\n-    puts \"----------------------------------------------------------------\"\n-  end\n+if branch_name || tag_name\n+  puts \"----------------------------------------------------------------\"\n+end\n \n-  show_shortlog(base, head)\n-  show_diff(patch, merge_base_id, head_id)\n+show_shortlog(base, head)\n+show_diff(patch, merge_base_id, head_id)\n \n-  if ! ref\n-    $stderr.puts \"warn: No branch of #{url} is at:\"\n-    $stderr.puts \"warn:   %s: %s'\" % [find_unique_abbrev(head_id, DEFAULT_ABBREV), head_summary]\n-    $stderr.puts \"warn: Are you sure you pushed '#{abbr(head_ref)}' there?\"\n-    status = 1\n-  end\n-rescue CommandError\n-  status = 1\n+if ! ref\n+  $stderr.puts \"warn: No branch of #{url} is at:\"\n+  $stderr.puts \"warn:   %s: %s'\" % [find_unique_abbrev(head_id, DEFAULT_ABBREV), head_summary]\n+  $stderr.puts \"warn: Are you sure you pushed '#{abbr(head_ref)}' there?\"\n+  exit 1\n end\n-\n-exit status\n-- \n1.8.4-fc\n"},{"id":"228419","messageId":"1380405849-13000-43-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 42/44] ruby: remove GIT_PAGER from environment","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:04:07Z","receivedAt":"2013-09-28T22:04:07Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"We are not calling any git commands any more.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n git-request-pull.rb | 2 --\n 1 file changed, 2 deletions(-)\n\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\nindex fde8d1a..941ff72 100755\n--- a/git-request-pull.rb\n+++ b/git-request-pull.rb\n@@ -2,8 +2,6 @@\n \n require 'date'\n \n-ENV['GIT_PAGER'] =\n-\n patch = nil\n \n def usage\n-- \n1.8.4-fc\n"},{"id":"228420","messageId":"1380405849-13000-44-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 43/44] ruby: add simpler option parser","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:04:08Z","receivedAt":"2013-09-28T22:04:08Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n\nNotes:\n    We could use Git's internap parseopt, but the resulting code in Ruby wouldn't\n    look very good, and the complexity to bind it wouldn't be trivial either (I\n    tried).\n    \n    Instead, let's use the advantage of Ruby blocks to set the variables to their\n    right values and try to keep the code simple, and add more features as they are\n    needed later on.\n\n git-rb-setup.rb  |  75 ++++++++++++++++++++++++++++++++\n t/t10000-ruby.sh | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 203 insertions(+)\n\ndiff --git a/git-rb-setup.rb b/git-rb-setup.rb\nindex 6f283da..cd2502f 100644\n--- a/git-rb-setup.rb\n+++ b/git-rb-setup.rb\n@@ -32,3 +32,78 @@ class String\n     return self[prefix.length..-1]\n   end\n end\n+\n+class ParseOpt\n+  attr_writer :usage\n+\n+  class Option\n+    attr_reader :short, :long, :help\n+\n+    def initialize(short, long, help, &block)\n+      @block = block\n+      @short = short\n+      @long = long\n+      @help = help\n+    end\n+\n+    def call(v)\n+      @block.call(v)\n+    end\n+  end\n+\n+  def initialize\n+    @list = {}\n+  end\n+\n+  def on(short = nil, long = nil, help: nil, &block)\n+    opt = Option.new(short, long, help, &block)\n+    @list[short] = opt if short\n+    @list[long] = opt if long\n+  end\n+\n+  def parse\n+    if ARGV.member?('-h') or ARGV.member?('--help')\n+      usage\n+      exit 0\n+    end\n+    seen_dash = false\n+    ARGV.delete_if do |cur|\n+      opt = val = nil\n+      next false if cur[0] != '-' or seen_dash\n+      case cur\n+      when '--'\n+        seen_dash = true\n+        next true\n+      when /^--no-(.+)$/\n+        opt = @list[$1]\n+        val = false\n+      when /^-([^-])(.+)?$/, /^--(.+?)(?:=(.+))?$/\n+        opt = @list[$1]\n+        val = $2 || true\n+      end\n+      if opt\n+        opt.call(val)\n+        true\n+      else\n+        usage\n+        exit 1\n+      end\n+    end\n+  end\n+\n+  def usage\n+    def fmt(prefix, str)\n+      return str ? prefix + str : nil\n+    end\n+    puts 'usage: %s' % @usage\n+    @list.values.uniq.each do |opt|\n+      s = '    '\n+      s << ''\n+      s << [fmt('-', opt.short), fmt('--', opt.long)].compact.join(', ')\n+      s << ''\n+      s << '%*s%s' % [26 - s.size, '', opt.help] if opt.help\n+      puts s\n+    end\n+  end\n+\n+end\ndiff --git a/t/t10000-ruby.sh b/t/t10000-ruby.sh\nindex e5a397c..a365849 100755\n--- a/t/t10000-ruby.sh\n+++ b/t/t10000-ruby.sh\n@@ -102,4 +102,132 @@ test_expect_success 'test Commit' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'test ParseOpt' '\n+\tcat > parse-script <<\"EOF\"\n+\t$str = \"default\"\n+\t$num = 0\n+\t$bool = false\n+\n+\topts = ParseOpt.new\n+\topts.usage = \"git foo\"\n+\n+\topts.on(\"b\", \"bool\", help: \"Boolean\") do |v|\n+\t  $bool = v\n+\tend\n+\n+\topts.on(\"s\", \"string\", help: \"String\") do |v|\n+\t  $str = v\n+\tend\n+\n+\topts.on(\"n\", \"number\", help: \"Number\") do |v|\n+\t  $num = v.to_i\n+\tend\n+\n+\topts.parse\n+\n+\tp(ARGV)\n+\tp({ :bool => $bool, :str => $str, :num => $num })\n+\tEOF\n+\n+\tgit ruby parse-script > actual &&\n+\tcat > expected <<-EOF &&\n+\t[]\n+\t{:bool=>false, :str=>\"default\", :num=>0}\n+\tEOF\n+\ttest_cmp expected actual &&\n+\n+\tgit ruby parse-script --bool > actual &&\n+\tcat > expected <<-EOF &&\n+\t[]\n+\t{:bool=>true, :str=>\"default\", :num=>0}\n+\tEOF\n+\ttest_cmp expected actual &&\n+\n+\tgit ruby parse-script -b > actual &&\n+\tcat > expected <<-EOF &&\n+\t[]\n+\t{:bool=>true, :str=>\"default\", :num=>0}\n+\tEOF\n+\ttest_cmp expected actual &&\n+\n+\tgit ruby parse-script --string=foo > actual &&\n+\tcat > expected <<-EOF &&\n+\t[]\n+\t{:bool=>false, :str=>\"foo\", :num=>0}\n+\tEOF\n+\ttest_cmp expected actual &&\n+\n+\tgit ruby parse-script -sfoo > actual &&\n+\tcat > expected <<-EOF &&\n+\t[]\n+\t{:bool=>false, :str=>\"foo\", :num=>0}\n+\tEOF\n+\ttest_cmp expected actual &&\n+\n+\tgit ruby parse-script --number=10 > actual &&\n+\tcat > expected <<-EOF &&\n+\t[]\n+\t{:bool=>false, :str=>\"default\", :num=>10}\n+\tEOF\n+\ttest_cmp expected actual &&\n+\n+\tgit ruby parse-script --bool --string=bar --number=-20 > actual &&\n+\tcat > expected <<-EOF &&\n+\t[]\n+\t{:bool=>true, :str=>\"bar\", :num=>-20}\n+\tEOF\n+\ttest_cmp expected actual &&\n+\n+\tgit ruby parse-script --help > actual &&\n+\tcat > expected <<-EOF &&\n+\tusage: git foo\n+\t    -b, --bool            Boolean\n+\t    -s, --string          String\n+\t    -n, --number          Number\n+\tEOF\n+\ttest_cmp expected actual &&\n+\n+\tgit ruby parse-script --help > actual &&\n+\tcat > expected <<-EOF &&\n+\tusage: git foo\n+\t    -b, --bool            Boolean\n+\t    -s, --string          String\n+\t    -n, --number          Number\n+\tEOF\n+\ttest_cmp expected actual &&\n+\n+\ttest_must_fail git ruby parse-script --bad > actual &&\n+\tcat > expected <<-EOF &&\n+\tusage: git foo\n+\t    -b, --bool            Boolean\n+\t    -s, --string          String\n+\t    -n, --number          Number\n+\tEOF\n+\ttest_cmp expected actual &&\n+\n+\tgit ruby parse-script one --bool two --string=bar three --number=-20 mambo > actual &&\n+\tcat > expected <<-EOF &&\n+\t[\"one\", \"two\", \"three\", \"mambo\"]\n+\t{:bool=>true, :str=>\"bar\", :num=>-20}\n+\tEOF\n+\ttest_cmp expected actual &&\n+\n+\tgit ruby parse-script one --bool two -- --three four > actual &&\n+\tcat > expected <<-EOF &&\n+\t[\"one\", \"two\", \"--three\", \"four\"]\n+\t{:bool=>true, :str=>\"default\", :num=>0}\n+\tEOF\n+\ttest_cmp expected actual &&\n+\n+\tgit ruby parse-script one --bool --no-bool > actual &&\n+\tcat > expected <<-EOF &&\n+\t[\"one\"]\n+\t{:bool=>false, :str=>\"default\", :num=>0}\n+\tEOF\n+\tcat actual\n+\ttest_cmp expected actual &&\n+\n+\ttrue\n+'\n+\n test_done\n-- \n1.8.4-fc\n"},{"id":"228421","messageId":"1380405849-13000-45-git-send-email-felipe.contreras@gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 44/44] request-pull: rewrite to C","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T22:04:09Z","receivedAt":"2013-09-28T22:04:09Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n Makefile               |   3 +-\n builtin.h              |   1 +\n builtin/request-pull.c | 275 +++++++++++++++++++++++++++++++++++++++++++++++++\n git-request-pull.rb    | 199 -----------------------------------\n git.c                  |   1 +\n 5 files changed, 278 insertions(+), 201 deletions(-)\n create mode 100644 builtin/request-pull.c\n delete mode 100755 git-request-pull.rb\n\ndiff --git a/Makefile b/Makefile\nindex 709c087..edbceea 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -492,9 +492,7 @@ SCRIPT_PYTHON += git-remote-testpy.py\n SCRIPT_PYTHON += git-p4.py\n \n SCRIPT_RUBY += git-rb-setup.rb\n-SCRIPT_RUBY += git-request-pull.rb\n \n-RUBY_PROGRAMS += git-request-pull$X\n PROGRAMS += $(RUBY_PROGRAMS)\n \n NO_INSTALL += git-remote-testgit\n@@ -983,6 +981,7 @@ BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/remote-ext.o\n BUILTIN_OBJS += builtin/remote-fd.o\n BUILTIN_OBJS += builtin/replace.o\n+BUILTIN_OBJS += builtin/request-pull.o\n BUILTIN_OBJS += builtin/rerere.o\n BUILTIN_OBJS += builtin/reset.o\n BUILTIN_OBJS += builtin/rev-list.o\ndiff --git a/builtin.h b/builtin.h\nindex 8afa2de..7db356e 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -103,6 +103,7 @@ extern int cmd_remote(int argc, const char **argv, const char *prefix);\n extern int cmd_remote_ext(int argc, const char **argv, const char *prefix);\n extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);\n extern int cmd_repo_config(int argc, const char **argv, const char *prefix);\n+extern int cmd_request_pull(int argc, const char **argv, const char *prefix);\n extern int cmd_rerere(int argc, const char **argv, const char *prefix);\n extern int cmd_reset(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_list(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/request-pull.c b/builtin/request-pull.c\nnew file mode 100644\nindex 0000000..a8f7f04\n--- /dev/null\n+++ b/builtin/request-pull.c\n@@ -0,0 +1,275 @@\n+#include \"cache.h\"\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+#include \"revision.h\"\n+#include \"refs.h\"\n+#include \"remote.h\"\n+#include \"transport.h\"\n+#include \"branch.h\"\n+#include \"shortlog.h\"\n+#include \"diff.h\"\n+#include \"log-tree.h\"\n+\n+static const char *tag_name;\n+\n+static const char *const pull_request_usage[] = {\n+\tN_(\"git request-pull [options] start url [end]\"),\n+\tNULL\n+};\n+\n+static int describe_cb(const char *name, const unsigned char *sha1, int flags, void *cb_data)\n+{\n+\tunsigned char peel_sha1[20];\n+\tif (prefixcmp(name, \"refs/tags/\"))\n+\t\treturn 0;\n+\tpeel_ref(name, peel_sha1);\n+\tif (hashcmp(peel_sha1, cb_data))\n+\t\treturn 0;\n+\ttag_name = skip_prefix(name, \"refs/tags/\");\n+\treturn 1;\n+}\n+\n+const char *describe(const char *head)\n+{\n+\tunsigned char sha1[20];\n+\tget_sha1(head, sha1);\n+\tfor_each_ref(describe_cb, sha1);\n+\treturn tag_name;\n+}\n+\n+const char *abbr(const char *name)\n+{\n+\treturn name + (\n+\t\t\t!prefixcmp(name, \"refs/heads/\") ? 11 :\n+\t\t\t!prefixcmp(name, \"refs/tags/\") ? 5 :\n+\t\t\t0);\n+}\n+\n+const char *get_ref(struct transport *transport, const char *head_ref, const unsigned char *head_id)\n+{\n+\tconst struct ref *refs, *e;\n+\tconst char *found = NULL;\n+\tint deref;\n+\n+\trefs = transport_get_remote_refs(transport);\n+\n+\tfor (e = refs; e; e = e->next) {\n+\t\tif (hashcmp(e->old_sha1, head_id))\n+\t\t\tcontinue;\n+\n+\t\tderef = !suffixcmp(e->name, \"^{}\");\n+\t\tfound = abbr(e->name);\n+\t\tif (deref && tag_name && !prefixcmp(e->name + 10, tag_name))\n+\t\t\tbreak;\n+\t\tif (head_ref && !strcmp(e->name, head_ref))\n+\t\t\tbreak;\n+\t}\n+\tif (!found)\n+\t\treturn NULL;\n+\treturn xstrndup(found, strlen(found) - (deref ? 3 : 0));\n+}\n+\n+static const char *show_ident_date(const struct ident_split *ident,\n+\t\t\t\t   enum date_mode mode)\n+{\n+\tunsigned long date = 0;\n+\tint tz = 0;\n+\n+\tif (ident->date_begin && ident->date_end)\n+\t\tdate = strtoul(ident->date_begin, NULL, 10);\n+\tif (ident->tz_begin && ident->tz_end)\n+\t\ttz = strtol(ident->tz_begin, NULL, 10);\n+\treturn show_date(date, tz, mode);\n+}\n+\n+static void parse_buffer(const char *buffer, const char **summary, const char **date)\n+{\n+\tstruct strbuf subject = STRBUF_INIT;\n+\tstruct ident_split s;\n+\tconst char *c, *e;\n+\n+\tc = strstr(buffer, \"\\ncommitter \") + 11;\n+\te = strchr(c, '\\n');\n+\tif (!split_ident_line(&s, c, e - c))\n+\t\t*date = show_ident_date(&s, DATE_ISO8601);\n+\n+\tc = strstr(c, \"\\n\\n\") + 2;\n+\tformat_subject(&subject, c, \" \");\n+\t*summary = strbuf_detach(&subject, NULL);\n+}\n+\n+static void show_shortlog(const char *base, const char *head)\n+{\n+\tstruct commit *commit;\n+\tstruct rev_info revs;\n+\tstruct shortlog log;\n+\tconst char *args[3];\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\n+\tstrbuf_addf(&tmp, \"^%s\", base);\n+\n+\targs[1] = tmp.buf;\n+\targs[2] = head;\n+\n+\tinit_revisions(&revs, NULL);\n+\tsetup_revisions(3, args, &revs, NULL);\n+\n+\tstrbuf_release(&tmp);\n+\n+\tshortlog_init(&log);\n+\tprepare_revision_walk(&revs);\n+\twhile ((commit = get_revision(&revs)))\n+\t\tshortlog_add_commit(&log, commit);\n+\tshortlog_output(&log);\n+}\n+\n+static void show_diff(int patch, const unsigned char *base, const unsigned char *head)\n+{\n+\tstruct rev_info revs;\n+\tconst char *args[3];\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\n+\tstrbuf_addf(&tmp, \"^%s\", sha1_to_hex(base));\n+\n+\targs[1] = tmp.buf;\n+\targs[2] = sha1_to_hex(head);\n+\n+\tinit_revisions(&revs, NULL);\n+\tsetup_revisions(3, args, &revs, NULL);\n+\trevs.diffopt.stat_width = -1;\n+\trevs.diffopt.stat_graph_width = -1;\n+\trevs.diffopt.output_format = patch ? DIFF_FORMAT_PATCH : DIFF_FORMAT_DIFFSTAT;\n+\trevs.diffopt.output_format |= DIFF_FORMAT_SUMMARY;\n+\trevs.diffopt.detect_rename = DIFF_DETECT_RENAME;\n+\trevs.diffopt.flags |= DIFF_OPT_RECURSIVE;\n+\n+\tstrbuf_release(&tmp);\n+\tdiff_tree_sha1(base, head, \"\", &revs.diffopt);\n+\tlog_tree_diff_flush(&revs);\n+}\n+\n+int cmd_request_pull(int argc, const char **argv, const char *prefix)\n+{\n+\tint patch;\n+\tconst char *base, *url, *head;\n+\tchar *head_ref;\n+\tchar *branch_name = NULL;\n+\tstruct strbuf branch_desc = STRBUF_INIT;\n+\tconst char *ref;\n+\tint status = 0;\n+\tunsigned char head_id[20], base_id[20];\n+\tunsigned char *merge_base_id;\n+\tstruct commit *base_commit, *head_commit, *merge_base_commit;\n+\tstruct commit_list *merge_bases;\n+\tconst char *merge_base_summary, *merge_base_date;\n+\tconst char *head_summary, *head_date;\n+\tstruct remote *remote;\n+\tstruct transport *transport;\n+\n+\tconst struct option options[] = {\n+\t\tOPT_BOOL('p', NULL, &patch, N_(\"show patch text as well\")),\n+\t\tOPT_END(),\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, pull_request_usage, 0);\n+\n+\tbase = argv[0];\n+\turl = argv[1];\n+\thead = argv[2] ? argv[2] : \"HEAD\";\n+\tstatus = 0;\n+\n+\tif (!base || !url)\n+\t\tusage_with_options(pull_request_usage, options);\n+\n+\tif (dwim_ref(head, strlen(head), head_id, &head_ref) == 1) {\n+\t\tif (!prefixcmp(head_ref, \"refs/heads/\")) {\n+\t\t\tbranch_name = head_ref + 11;\n+\t\t\tif (read_branch_desc(&branch_desc, branch_name) || !branch_desc.len)\n+\t\t\t\tbranch_name = NULL;\n+\t\t}\n+\t}\n+\n+\tdescribe(head);\n+\n+\tget_sha1(base, base_id);\n+\tbase_commit = lookup_commit_reference(base_id);\n+\tif (!base_commit)\n+\t\tdie(\"Not a valid revision: %s\", base);\n+\n+\thead_commit = lookup_commit_reference(head_id);\n+\tif (!head_commit)\n+\t\tdie(\"Not a valid revision: %s\", head);\n+\n+\tmerge_bases = get_merge_bases(base_commit, head_commit, 0);\n+\tif (!merge_bases)\n+\t\tdie(\"No commits in common between %s and %s\", base, head);\n+\n+\tmerge_base_commit = merge_bases->item;\n+\tmerge_base_id = merge_base_commit->object.sha1;\n+\n+\tremote = remote_get(url);\n+\turl = remote->url[0];\n+\ttransport = transport_get(remote, url);\n+\tref = get_ref(transport, strcmp(head_ref, \"HEAD\") ? head_ref : NULL, head_id);\n+\ttransport_disconnect(transport);\n+\n+\tparse_buffer(merge_base_commit->buffer, &merge_base_summary, &merge_base_date);\n+\tparse_buffer(head_commit->buffer, &head_summary, &head_date);\n+\n+\tprintf(\"The following changes since commit %s:\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"  %s (%s)\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"are available in the git repository at:\\n\"\n+\t\t\t\"\\n\",\n+\t\t\tsha1_to_hex(merge_base_id), merge_base_summary, merge_base_date);\n+\tprintf(\"  %s\", url);\n+\tif (ref)\n+\t\tprintf(\" %s\", ref);\n+\tprintf(\"\\n\");\n+\tprintf(\"\\n\"\n+\t\t\t\"for you to fetch changes up to %s:\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"  %s (%s)\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"----------------------------------------------------------------\\n\",\n+\t\t\tsha1_to_hex(head_id), head_summary, head_date);\n+\n+\tif (branch_name)\n+\t\tprintf(\"(from the branch description for %s local branch)\\n\\n%s\\n\", branch_name, branch_desc.buf);\n+\n+\tif (tag_name) {\n+\t\tvoid *buffer;\n+\t\tenum object_type type;\n+\t\tunsigned long size;\n+\t\tunsigned char sha1[20];\n+\t\tconst char *begin, *end;\n+\n+\t\tif (!ref || prefixcmp(ref, \"tags/\") || strcmp(ref + 5, tag_name)) {\n+\t\t\tfprintf(stderr, \"warn: You locally have %s but it does not (yet)\\n\", tag_name);\n+\t\t\tfprintf(stderr, \"warn: appear to be at %s\\n\", url);\n+\t\t\tfprintf(stderr, \"warn: Do you want to push it there, perhaps?\\n\");\n+\t\t}\n+\t\tget_sha1(tag_name, sha1);\n+\t\tbuffer = read_sha1_file(sha1, &type, &size);\n+\t\tbegin = strstr(buffer, \"\\n\\n\") + 2;\n+\t\tend = strstr(begin, \"-----BEGIN PGP \");\n+\t\tif (!end)\n+\t\t\tend = begin + strlen(begin);\n+\t\tprintf(\"%.*s\\n\", (int)(end - begin), begin);\n+\t}\n+\n+\tif (branch_name || tag_name)\n+\t\tputs(\"----------------------------------------------------------------\");\n+\n+\tshow_shortlog(base, head);\n+\tshow_diff(patch, merge_base_id, head_id);\n+\n+\tif (!ref) {\n+\t\tfprintf(stderr, \"warn: No branch of %s is at:\\n\", url);\n+\t\tfprintf(stderr, \"warn:   %s: %s\\n\", find_unique_abbrev(head_id, DEFAULT_ABBREV), head_summary);\n+\t\tfprintf(stderr, \"warn: Are you sure you pushed '%s' there?\\n\", head);\n+\t\tstatus = 1;\n+\t}\n+\treturn status;\n+}\ndiff --git a/git-request-pull.rb b/git-request-pull.rb\ndeleted file mode 100755\nindex 941ff72..0000000\n--- a/git-request-pull.rb\n+++ /dev/null\n@@ -1,199 +0,0 @@\n-#!git ruby\n-\n-require 'date'\n-\n-patch = nil\n-\n-def usage\n-  puts <<EOF\n-usage: git request-pull [options] start url [end]\n-\n-    -p                    show patch text as well\n-\n-EOF\n-  exit 1\n-end\n-\n-def read_branch_desc(name)\n-  git_config() do |key, value|\n-    return value if key == \"branch.#{name}.description\"\n-  end\n-  return nil\n-end\n-\n-def describe(rev)\n-  for_each_ref() do |name, sha1, flags|\n-    next unless name.start_with?('refs/tags/')\n-    next unless peel_ref(name) == get_sha1(rev)\n-    return name.skip_prefix('refs/tags/')\n-  end\n-  return nil\n-end\n-\n-def abbr(ref)\n-  if (ref =~ %r{^refs/heads/(.*)} || ref =~ %r{^refs/(tags/.*)})\n-    return $1\n-  end\n-  return ref\n-end\n-\n-# $head is the token given from the command line, and $tag_name, if\n-# exists, is the tag we are going to show the commit information for.\n-# If that tag exists at the remote and it points at the commit, use it.\n-# Otherwise, if a branch with the same name as $head exists at the remote\n-# and their values match, use that instead.\n-#\n-# Otherwise find a random ref that matches $head_id.\n-\n-def get_ref(transport, head_ref, head_id, tag_name)\n-  found = nil\n-  transport.get_remote_refs().each do |e|\n-    sha1 = e.old_sha1\n-    next unless sha1 == head_id\n-    ref, deref = e.name.scan(/^(\\S+?)(\\^\\{\\})?$/).first\n-    found = abbr(ref)\n-    break if (deref && ref == \"refs/tags/#{tag_name}\")\n-    break if ref == head_ref\n-  end\n-  return found\n-end\n-\n-def parse_buffer(buffer)\n-  summary = []\n-  date = msg = nil\n-  header, body = buffer.split(\"\\n\\n\", 2)\n-  header.each_line do |line|\n-    case line\n-    when /^committer ([^<>]+) <(\\S+)> (.+)$/\n-      date = DateTime.strptime($3, '%s %z')\n-    end\n-  end\n-  body.each_line do |l|\n-    break if (l.strip.empty?)\n-    summary << l.chomp\n-  end\n-  summary = summary.join(' ')\n-  date = date.strftime('%F %T %z')\n-  return [summary, date]\n-end\n-\n-def show_shortlog(base, head)\n-  rev = Git::RevInfo.setup(nil, ['^' + base, head], nil)\n-  shortlog(rev.to_a)\n-end\n-\n-def show_diff(patch, base, head)\n-  rev = Git::RevInfo.setup(nil, ['^' + sha1_to_hex(base), sha1_to_hex(head)], nil)\n-  rev.diffopt.stat_width = -1\n-  rev.diffopt.stat_graph_width = -1\n-  rev.diffopt.output_format = patch ? DIFF_FORMAT_PATCH : DIFF_FORMAT_DIFFSTAT\n-  rev.diffopt.output_format |= DIFF_FORMAT_SUMMARY\n-  rev.diffopt.detect_rename = DIFF_DETECT_RENAME\n-  rev.diffopt.flags |= DIFF_OPT_RECURSIVE\n-\n-  diff_tree_sha1(base, head, \"\", rev.diffopt)\n-  log_tree_diff_flush(rev)\n-end\n-\n-until ARGV.empty?\n-  case ARGV.first\n-  when '-p'\n-    patch = '-p'\n-  when '--'\n-    ARGV.shift\n-    break\n-  when /^-/\n-    usage\n-  else\n-    break\n-  end\n-  ARGV.shift\n-end\n-\n-base = ARGV[0]\n-url = ARGV[1]\n-head = ARGV[2] || 'HEAD'\n-branch_name = branch_desc = nil\n-\n-usage unless base or url\n-\n-_, _, head_ref = dwim_ref(head)\n-\n-if head_ref.start_with?('refs/heads')\n-  branch_name = head_ref[11..-1]\n-  branch_desc = read_branch_desc(branch_name)\n-  branch_name = nil if not branch_desc\n-end\n-\n-tag_name = describe(head)\n-\n-base_id = get_sha1(\"#{base}^0\")\n-die \"Not a valid revision: #{base}\" unless base_id\n-\n-head_id = get_sha1(\"#{head}^0\")\n-die \"Not a valid revision: #{head}\" unless head_id\n-\n-base_commit = Git::Commit.get(base_id)\n-head_commit = Git::Commit.get(head_id)\n-\n-merge_bases = get_merge_bases([base_commit, head_commit], 0);\n-die \"No commits in common between #{base} and #{head}\" unless merge_bases\n-\n-merge_base_id = merge_bases.first.sha1\n-merge_base_commit = Git::Commit.get(merge_base_id)\n-\n-remote = remote_get(url)\n-transport = transport_get(remote, nil)\n-\n-ref = get_ref(transport, head_ref != \"HEAD\" ? head_ref : nil, head_id, tag_name)\n-url = remote.url.first\n-\n-merge_base_summary, merge_base_date = parse_buffer(merge_base_commit.buffer)\n-head_summary, head_date = parse_buffer(head_commit.buffer)\n-\n-puts \"The following changes since commit %s:\n-\n-  %s (%s)\n-\n-are available in the git repository at:\n-\n-\" % [merge_base_commit, merge_base_summary, merge_base_date]\n-puts \"  #{url}\" + (ref ? \" #{ref}\" : \"\")\n-puts \"\n-for you to fetch changes up to %s:\n-\n-  %s (%s)\n-\n-----------------------------------------------------------------\n-\" % [head_commit, head_summary, head_date]\n-\n-if branch_name\n-  puts \"(from the branch description for #{branch_name} local branch)\"\n-  puts\n-  puts branch_desc\n-end\n-\n-if tag_name\n-  if ref != \"tags/#{tag_name}\"\n-    $stderr.puts \"warn: You locally have #{tag_name} but it does not (yet)\"\n-    $stderr.puts \"warn: appear to be at #{url}\"\n-    $stderr.puts \"warn: Do you want to push it there, perhaps?\"\n-  end\n-  buffer, _ = read_sha1_file(get_sha1(tag_name))\n-  puts buffer.scan(/(?:\\n\\n)(.+)(?:-----BEGIN PGP )?/m).first\n-  puts\n-end\n-\n-if branch_name || tag_name\n-  puts \"----------------------------------------------------------------\"\n-end\n-\n-show_shortlog(base, head)\n-show_diff(patch, merge_base_id, head_id)\n-\n-if ! ref\n-  $stderr.puts \"warn: No branch of #{url} is at:\"\n-  $stderr.puts \"warn:   %s: %s'\" % [find_unique_abbrev(head_id, DEFAULT_ABBREV), head_summary]\n-  $stderr.puts \"warn: Are you sure you pushed '#{abbr(head_ref)}' there?\"\n-  exit 1\n-end\ndiff --git a/git.c b/git.c\nindex 2025f77..d67d3fb 100644\n--- a/git.c\n+++ b/git.c\n@@ -398,6 +398,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"remote-fd\", cmd_remote_fd },\n \t\t{ \"replace\", cmd_replace, RUN_SETUP },\n \t\t{ \"repo-config\", cmd_repo_config, RUN_SETUP_GENTLY },\n+\t\t{ \"request-pull\", cmd_request_pull, RUN_SETUP_GENTLY },\n \t\t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t\t{ \"reset\", cmd_reset, RUN_SETUP },\n \t\t{ \"rev-list\", cmd_rev_list, RUN_SETUP },\n-- \n1.8.4-fc\n"},{"id":"228427","messageId":"524765DE.9030009@googlemail.com","threadId":"35044","inReplyTo":"1380405849-13000-43-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 42/44] ruby: remove GIT_PAGER from environment","fromName":"Stefan Beller","fromEmail":"stefanbeller@googlemail.com","sentAt":"2013-09-28T23:27:26Z","receivedAt":"2013-09-28T23:27:26Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On 09/29/2013 12:04 AM, Felipe Contreras wrote:\n> We are not calling any git commands any more.\n> \n> Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n> ---\n>  git-request-pull.rb | 2 --\n>  1 file changed, 2 deletions(-)\n> \n> diff --git a/git-request-pull.rb b/git-request-pull.rb\n> index fde8d1a..941ff72 100755\n> --- a/git-request-pull.rb\n> +++ b/git-request-pull.rb\n> @@ -2,8 +2,6 @@\n>  \n>  require 'date'\n>  \n> -ENV['GIT_PAGER'] =\n> -\n>  patch = nil\n>  \n>  def usage\n> \n\nCould this be squashed into the patch introducing the ENV?\n(I assume that patch is among the 44 patches?)\n\nThanks,\nStefan\n"},{"id":"228428","messageId":"CAMP44s0v5tj=e1o0tze00OzRM1YNuMU9YaVvtav02Eoj8+O-nw@mail.gmail.com","threadId":"35044","inReplyTo":"524765DE.9030009@googlemail.com","subject":"Re: [PATCH v2 42/44] ruby: remove GIT_PAGER from environment","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-28T23:33:38Z","receivedAt":"2013-09-28T23:33:38Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Sat, Sep 28, 2013 at 6:27 PM, Stefan Beller\n<stefanbeller@googlemail.com> wrote:\n> On 09/29/2013 12:04 AM, Felipe Contreras wrote:\n>> We are not calling any git commands any more.\n>>\n>> Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n>> ---\n>>  git-request-pull.rb | 2 --\n>>  1 file changed, 2 deletions(-)\n>>\n>> diff --git a/git-request-pull.rb b/git-request-pull.rb\n>> index fde8d1a..941ff72 100755\n>> --- a/git-request-pull.rb\n>> +++ b/git-request-pull.rb\n>> @@ -2,8 +2,6 @@\n>>\n>>  require 'date'\n>>\n>> -ENV['GIT_PAGER'] =\n>> -\n>>  patch = nil\n>>\n>>  def usage\n>>\n>\n> Could this be squashed into the patch introducing the ENV?\n> (I assume that patch is among the 44 patches?)\n\nIt could be, but when ENV['GIT_PAGER'] was introduced, there were 'git\nfoo' commands being called, so they would get a different $GIT_PAGER\nif we do so, which would be a change from the original script.\n\nTo be honest I don't know what that code is trying to achieve, but to\nerr on the side of caution I'm leaving the code close to the original.\n\n-- \nFelipe Contreras\n"},{"id":"228434","messageId":"CALkWK0nfP5CZYSvqrYeBAqkjCE_Zg-QenRcNZcP3-K7qPrZhoQ@mail.gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-1-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 00/44] Ruby support","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-09-29T04:13:54Z","receivedAt":"2013-09-29T04:13:54Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Felipe Contreras wrote:\n> Felipe Contreras (44):\n\nThanks! In the meantime, can you publish this work somewhere so we can\neasily try it out?\n"},{"id":"228435","messageId":"CAMP44s0x4ivR58A2Xf9RhOUn+dg=vKtQCoFtn_p3s++eM8ipfA@mail.gmail.com","threadId":"35044","inReplyTo":"CALkWK0nfP5CZYSvqrYeBAqkjCE_Zg-QenRcNZcP3-K7qPrZhoQ@mail.gmail.com","subject":"Re: [PATCH v2 00/44] Ruby support","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-29T04:20:05Z","receivedAt":"2013-09-29T04:20:05Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Sat, Sep 28, 2013 at 11:13 PM, Ramkumar Ramachandra\n<artagnon@gmail.com> wrote:\n> Felipe Contreras wrote:\n>> Felipe Contreras (44):\n>\n> Thanks! In the meantime, can you publish this work somewhere so we can\n> easily try it out?\n\nSure:\nhttps://github.com/felipec/git/commits/fc/ruby\n\n-- \nFelipe Contreras\n"},{"id":"228437","messageId":"CALkWK0m+LW-R-WJ6DmQkf1GKbCgvpzaGc2=rCNnmiPkf8x_qLg@mail.gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-2-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 01/44] Add support for ruby commands","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-09-29T04:30:34Z","receivedAt":"2013-09-29T04:30:34Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Felipe Contreras wrote:\n> +test_expect_success 'argument passing' '\n> +       cat > script <<-\"EOF\" &&\n> +       p($0)\n> +       p(ARGV)\n> +       EOF\n> +       git ruby script foo bar > actual &&\n> +       cat > expected <<-EOF &&\n> +       \"script\"\n> +       [\"foo\", \"bar\"]\n> +       EOF\n> +       test_cmp expected actual\n> +'\n\nSo, git-ruby is a new builtin that's just like a ruby interpreter,\nexcept that it has certain git-related functions defined: you pass it\nsource code on stdin, and it returns the result on stdout. I wonder\nwhat the purpose of passing arguments to it is though.\n\n> +test_expect_success 'test for_each_ref()' '\n> +       test_commit foo &&\n> +       git ruby > actual <<-EOF &&\n> +       for_each_ref() do |name, sha1, flags|\n> +               puts \"%s: %s\" % [name, sha1_to_hex(sha1)]\n> +       end\n> +       EOF\n> +       git for-each-ref --format=\"%(refname): %(objectname)\" > expected &&\n> +       test_cmp expected actual\n> +'\n\nI find it interesting that you chose to implement for-each-ref in the\ninitial version. It currently takes no arguments, but we can always\nextend it to do ref-hierarchy filtering like f-e-r in the future.\n"},{"id":"228438","messageId":"CALkWK0=XSxiz+MoU2H7Zxo4gSRvs+C5fGpnD2ioBvrXzdLpmww@mail.gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-3-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 02/44] ruby: add support for internal ruby programs","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-09-29T04:40:49Z","receivedAt":"2013-09-29T04:40:49Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Felipe Contreras wrote:\n> +$(RUBY_PROGRAMS): git-ruby$X\n> +       $(QUIET_BUILT_IN)$(RM) $@ && \\\n> +       ln >lt; $@ 2>/dev/null || \\\n> +       ln -s >lt; $@ 2>/dev/null || \\\n> +       cp >lt; $@\n\nWhy so many fallbacks? Will the hard-link (the first ln) ever fail?\n\n> diff --git a/ruby.c b/ruby.c\n> index ee6a0e7..339e376 100644\n> --- a/ruby.c\n> +++ b/ruby.c\n> @@ -52,5 +52,22 @@ static int run_ruby_command(const char *cmd, int argc, const char **argv)\n>\n>  int main(int argc, const char **argv)\n>  {\n> -       return run_ruby_command(argv[1], argc, argv);\n> +       if (!strcmp(argv[0], \"git-ruby\")) {\n> +               return run_ruby_command(argv[1], argc, argv);\n> +       } else {\n> +               const char *cmd = argv[0];\n> +               static char buf[PATH_MAX + 1];\n> +               const char *args[argc + 1];\n> +               int i;\n> +\n> +               snprintf(buf, PATH_MAX, \"%s/%s.rb\",\n> +                               git_exec_path(), basename((char *)cmd));\n> +\n> +               args[0] = \"git\";\n> +               args[1] = buf;\n> +               for (i = 0; i < argc - 1; i++)\n> +                       args[i + 2] = (char *)argv[i + 1];\n> +\n> +               return run_ruby_command(cmd, argc + 1, args);\n> +       }\n>  }\n\nCan you explain this in greater detail in your commit message? When I\npass an argument,\n\n  $ git ruby foo\n  git-ruby: No such file or directory -- foo (LoadError)\n\nI get this as before. How exactly will new ruby scripts be executed?\n(I can only guess at this point)\n"},{"id":"228439","messageId":"CALkWK0kDbx_9Sa8r8=55aYwfOtP15rkUik8gwEiHqR8ZgU8DYg@mail.gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-4-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 03/44] request-pull: fix annotated tag check","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-09-29T04:41:55Z","receivedAt":"2013-09-29T04:41:55Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Felipe Contreras wrote:\n> diff --git a/git-request-pull.sh b/git-request-pull.sh\n> index ebf1269..6348dac 100755\n> --- a/git-request-pull.sh\n> +++ b/git-request-pull.sh\n> @@ -89,7 +89,7 @@ find_matching_ref='\n>                 my ($sha1, $ref, $deref) = /^(\\S+)\\s+(\\S+?)(\\^\\{\\})?$/;\n>                 next unless ($sha1 eq $ARGV[1]);\n>                 $found = abbr($ref);\n> -               if ($deref && $ref eq \"tags/$ARGV[2]\") {\n> +               if ($deref && $ref eq \"refs/tags/$ARGV[2]\") {\n\nI've never used request-pull, but this seems correct.\n"},{"id":"228440","messageId":"CAMP44s0++uqqDc0YKO0bxkP3WfLRgVKYYfTVxJod_Kf23i=5zg@mail.gmail.com","threadId":"35044","inReplyTo":"CALkWK0m+LW-R-WJ6DmQkf1GKbCgvpzaGc2=rCNnmiPkf8x_qLg@mail.gmail.com","subject":"Re: [PATCH v2 01/44] Add support for ruby commands","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-29T04:47:28Z","receivedAt":"2013-09-29T04:47:28Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Sat, Sep 28, 2013 at 11:30 PM, Ramkumar Ramachandra\n<artagnon@gmail.com> wrote:\n> Felipe Contreras wrote:\n>> +test_expect_success 'argument passing' '\n>> +       cat > script <<-\"EOF\" &&\n>> +       p($0)\n>> +       p(ARGV)\n>> +       EOF\n>> +       git ruby script foo bar > actual &&\n>> +       cat > expected <<-EOF &&\n>> +       \"script\"\n>> +       [\"foo\", \"bar\"]\n>> +       EOF\n>> +       test_cmp expected actual\n>> +'\n>\n> So, git-ruby is a new builtin that's just like a ruby interpreter,\n> except that it has certain git-related functions defined: you pass it\n> source code on stdin, and it returns the result on stdout. I wonder\n> what the purpose of passing arguments to it is though.\n\ngit ruby is just like ruby, so you can pass the script from stdin, or\nyou can specify the script name.\n\n>> +test_expect_success 'test for_each_ref()' '\n>> +       test_commit foo &&\n>> +       git ruby > actual <<-EOF &&\n>> +       for_each_ref() do |name, sha1, flags|\n>> +               puts \"%s: %s\" % [name, sha1_to_hex(sha1)]\n>> +       end\n>> +       EOF\n>> +       git for-each-ref --format=\"%(refname): %(objectname)\" > expected &&\n>> +       test_cmp expected actual\n>> +'\n>\n> I find it interesting that you chose to implement for-each-ref in the\n> initial version. It currently takes no arguments, but we can always\n> extend it to do ref-hierarchy filtering like f-e-r in the future.\n\nIt takes no arguments because for_each_ref()(refs.h) takes no\narguments (that Ruby would need). builtin/for-each-ref.c uses\nfor_each_rawref()(refs.h), which is basically the same.\n\n-- \nFelipe Contreras\n"},{"id":"228441","messageId":"CAMP44s28B+UX1ofQb_sZfaPG2htvQ=W_EvScVbFgmR9P4SnvPg@mail.gmail.com","threadId":"35044","inReplyTo":"CALkWK0=XSxiz+MoU2H7Zxo4gSRvs+C5fGpnD2ioBvrXzdLpmww@mail.gmail.com","subject":"Re: [PATCH v2 02/44] ruby: add support for internal ruby programs","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-29T04:56:18Z","receivedAt":"2013-09-29T04:56:18Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Sat, Sep 28, 2013 at 11:40 PM, Ramkumar Ramachandra\n<artagnon@gmail.com> wrote:\n> Felipe Contreras wrote:\n>> +$(RUBY_PROGRAMS): git-ruby$X\n>> +       $(QUIET_BUILT_IN)$(RM) $@ && \\\n>> +       ln >lt; $@ 2>/dev/null || \\\n>> +       ln -s >lt; $@ 2>/dev/null || \\\n>> +       cp >lt; $@\n>\n> Why so many fallbacks? Will the hard-link (the first ln) ever fail?\n\nBecause that's what the code right on top of this is doing. I presume\nit would fail in file-systems that don't have hard-links.\n\n>> diff --git a/ruby.c b/ruby.c\n>> index ee6a0e7..339e376 100644\n>> --- a/ruby.c\n>> +++ b/ruby.c\n>> @@ -52,5 +52,22 @@ static int run_ruby_command(const char *cmd, int argc, const char **argv)\n>>\n>>  int main(int argc, const char **argv)\n>>  {\n>> -       return run_ruby_command(argv[1], argc, argv);\n>> +       if (!strcmp(argv[0], \"git-ruby\")) {\n>> +               return run_ruby_command(argv[1], argc, argv);\n>> +       } else {\n>> +               const char *cmd = argv[0];\n>> +               static char buf[PATH_MAX + 1];\n>> +               const char *args[argc + 1];\n>> +               int i;\n>> +\n>> +               snprintf(buf, PATH_MAX, \"%s/%s.rb\",\n>> +                               git_exec_path(), basename((char *)cmd));\n>> +\n>> +               args[0] = \"git\";\n>> +               args[1] = buf;\n>> +               for (i = 0; i < argc - 1; i++)\n>> +                       args[i + 2] = (char *)argv[i + 1];\n>> +\n>> +               return run_ruby_command(cmd, argc + 1, args);\n>> +       }\n>>  }\n>\n> Can you explain this in greater detail in your commit message? When I\n> pass an argument,\n>\n>   $ git ruby foo\n>   git-ruby: No such file or directory -- foo (LoadError)\n>\n> I get this as before. How exactly will new ruby scripts be executed?\n> (I can only guess at this point)\n\nIf you do:\n\n% git ruby foo\n\nIt's the same as\n\n% ruby foo\n\nYou need the script right there, as a file named \"foo\".\n\nHowever, this already works before this patch.\n\nWhat this patch does is enable:\n\n% git foo\n\nBut for this you need two things:\n\n1) A binary named git-foo in your path, that is a link to git-ruby,\nwhich is what the variable RUBY_PROGRAMS is for in the Makefile\n\n2) A script named git-foo.rb in your exec-path.\n\nSo basically \"git-foo\" is the same as \"git ruby $GIT_EXEC_PATH/git-foo.rb\".\n\n-- \nFelipe Contreras\n"},{"id":"228442","messageId":"CALkWK0kcu9YcOGgO45TqOYtsYd=g8GW=ELR=-SdgQJDcWTbaxQ@mail.gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-11-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 10/44] ruby: rewrite 'request-pull'","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-09-29T04:59:35Z","receivedAt":"2013-09-29T04:59:35Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Felipe Contreras wrote:\n>  Makefile            |   3 +-\n>  git-rb-setup.rb     |  17 ++++++\n>  git-request-pull.rb | 146 +++++++++++++++++++++++++++++++++++++++++++++++\n>  git-request-pull.sh | 160 ----------------------------------------------------\n\nExcellent: git-request-pull is now an ELF executable! Normally, one\nwould move the older script (git-request-pull.sh) into\ncontrib/examples during such a rewrite, although I don't see the point\nof doing that.\n\nThe proof is the pudding: all the request-pull tests pass.\n"},{"id":"228443","messageId":"CALkWK0nVHRV40VOBwtRnVsDcCpgZNQ-XtwL1syABE9ED30wuiA@mail.gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-12-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 11/44] ruby: request-pull: rewrite perl script","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-09-29T05:04:07Z","receivedAt":"2013-09-29T05:04:07Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Felipe Contreras wrote:\n>  git-request-pull.rb | 65 +++++++++++++++++++++++++----------------------------\n>  1 file changed, 30 insertions(+), 35 deletions(-)\n\nArguably, more pretty and terse. Again, all tests pass.\n\nLooks reasonable.\n"},{"id":"228444","messageId":"CAMP44s186x7uWh8-0hHK+3ye=-7UsdJSUZbFODG+bgKkS1PZwA@mail.gmail.com","threadId":"35044","inReplyTo":"CAMP44s28B+UX1ofQb_sZfaPG2htvQ=W_EvScVbFgmR9P4SnvPg@mail.gmail.com","subject":"Re: [PATCH v2 02/44] ruby: add support for internal ruby programs","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-29T05:07:58Z","receivedAt":"2013-09-29T05:07:58Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Sat, Sep 28, 2013 at 11:56 PM, Felipe Contreras\n<felipe.contreras@gmail.com> wrote:\n> On Sat, Sep 28, 2013 at 11:40 PM, Ramkumar Ramachandra\n\n>>> diff --git a/ruby.c b/ruby.c\n>>> index ee6a0e7..339e376 100644\n>>> --- a/ruby.c\n>>> +++ b/ruby.c\n>>> @@ -52,5 +52,22 @@ static int run_ruby_command(const char *cmd, int argc, const char **argv)\n>>>\n>>>  int main(int argc, const char **argv)\n>>>  {\n>>> -       return run_ruby_command(argv[1], argc, argv);\n>>> +       if (!strcmp(argv[0], \"git-ruby\")) {\n>>> +               return run_ruby_command(argv[1], argc, argv);\n>>> +       } else {\n>>> +               const char *cmd = argv[0];\n>>> +               static char buf[PATH_MAX + 1];\n>>> +               const char *args[argc + 1];\n>>> +               int i;\n>>> +\n>>> +               snprintf(buf, PATH_MAX, \"%s/%s.rb\",\n>>> +                               git_exec_path(), basename((char *)cmd));\n>>> +\n>>> +               args[0] = \"git\";\n>>> +               args[1] = buf;\n>>> +               for (i = 0; i < argc - 1; i++)\n>>> +                       args[i + 2] = (char *)argv[i + 1];\n>>> +\n>>> +               return run_ruby_command(cmd, argc + 1, args);\n>>> +       }\n>>>  }\n>>\n>> Can you explain this in greater detail in your commit message? When I\n>> pass an argument,\n>>\n>>   $ git ruby foo\n>>   git-ruby: No such file or directory -- foo (LoadError)\n>>\n>> I get this as before. How exactly will new ruby scripts be executed?\n>> (I can only guess at this point)\n>\n> If you do:\n>\n> % git ruby foo\n>\n> It's the same as\n>\n> % ruby foo\n>\n> You need the script right there, as a file named \"foo\".\n>\n> However, this already works before this patch.\n>\n> What this patch does is enable:\n>\n> % git foo\n>\n> But for this you need two things:\n>\n> 1) A binary named git-foo in your path, that is a link to git-ruby,\n> which is what the variable RUBY_PROGRAMS is for in the Makefile\n>\n> 2) A script named git-foo.rb in your exec-path.\n>\n> So basically \"git-foo\" is the same as \"git ruby $GIT_EXEC_PATH/git-foo.rb\".\n\nNote that this is for _internal_ Ruby programs, if you want \"git foo\"\nto run yours, or a third party script, you can do it like this:\n\ncat > git-foo <<EOF\n#!git ruby\n\n#ruby code\nEOF\n\nchmod +x and put it on your PATH, done :)\n\n-- \nFelipe Contreras\n"},{"id":"228445","messageId":"CALkWK0kSWDiKHDNcFFXHMa__YZXpL=YAL_zejvTRu7bAqUc6Vw@mail.gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-14-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 13/44] ruby: bind setup_git_directory()","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-09-29T05:11:47Z","receivedAt":"2013-09-29T05:11:47Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Felipe Contreras wrote:\n> +static inline VALUE cstr_to_str(const char *str)\n> +{\n> +       if (str == NULL)\n> +               return Qnil;\n> +       return rb_str_new2(str);\n> +}\n> +\n> +static VALUE git_rb_setup_git_directory(VALUE self)\n> +{\n> +       int nongit_ok;\n> +       const char *prefix;\n> +       prefix = setup_git_directory_gently(&nongit_ok);\n> +       return rb_ary_new3(2, cstr_to_str(prefix), INT2FIX(nongit_ok));\n> +}\n\nMost excellent. Goes to show how well ruby.h is written.\n\nI'm not very familiar with the interface, and am referring to\nhttp://www.ruby-doc.org/docs/ProgrammingRuby/html/ext_ruby.html -- let\nme know if there are some other sources.\n"},{"id":"228446","messageId":"CALkWK0mutBpqvu31uZARUFtGzN6zwP4A=915u=xBoc+PXQp+_A@mail.gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-15-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 14/44] ruby: bind dwim_ref()","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-09-29T05:17:58Z","receivedAt":"2013-09-29T05:17:58Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Felipe Contreras wrote:\n>  static void git_ruby_init(void)\n>  {\n>         rb_define_global_function(\"setup_git_directory\", git_rb_setup_git_directory, 0);\n>         rb_define_global_function(\"for_each_ref\", git_rb_for_each_ref, 0);\n> +       rb_define_global_function(\"dwim_ref\", git_rb_dwim_ref, 1);\n>  }\n\nAt this point, global functions is probably the way to go. We might\nlike to put them in classes to create a beautiful Rugged-inspired API\nin the future.\n"},{"id":"228447","messageId":"CAMP44s2Kd0xUXfRQ07qJ1yvXiUkOmXCMA67wrk4Vg9oN8TRMGQ@mail.gmail.com","threadId":"35044","inReplyTo":"CALkWK0mutBpqvu31uZARUFtGzN6zwP4A=915u=xBoc+PXQp+_A@mail.gmail.com","subject":"Re: [PATCH v2 14/44] ruby: bind dwim_ref()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-29T05:24:02Z","receivedAt":"2013-09-29T05:24:02Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Sun, Sep 29, 2013 at 12:17 AM, Ramkumar Ramachandra\n<artagnon@gmail.com> wrote:\n> Felipe Contreras wrote:\n>>  static void git_ruby_init(void)\n>>  {\n>>         rb_define_global_function(\"setup_git_directory\", git_rb_setup_git_directory, 0);\n>>         rb_define_global_function(\"for_each_ref\", git_rb_for_each_ref, 0);\n>> +       rb_define_global_function(\"dwim_ref\", git_rb_dwim_ref, 1);\n>>  }\n>\n> At this point, global functions is probably the way to go. We might\n> like to put them in classes to create a beautiful Rugged-inspired API\n> in the future.\n\nWe might, for certain things, but as I explained before, we can't\nreally do the same.\n\ntest1 = Rugged::Repository.new(\"test1\")\ntest2 = Rugged::Repository.new(\"test2\")\n\nHow are we supposed to do this with Git's code? If Git's code can only\nwork with one repository at a time, I think the Ruby bindings should\nreflect that as well.\n\n-- \nFelipe Contreras\n"},{"id":"228448","messageId":"CAMP44s2GFVxRgnR3ReM4mknpC5DXUZMBPgK+JoyOQwX0padsDg@mail.gmail.com","threadId":"35044","inReplyTo":"CALkWK0kSWDiKHDNcFFXHMa__YZXpL=YAL_zejvTRu7bAqUc6Vw@mail.gmail.com","subject":"Re: [PATCH v2 13/44] ruby: bind setup_git_directory()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-29T05:40:34Z","receivedAt":"2013-09-29T05:40:34Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Sun, Sep 29, 2013 at 12:11 AM, Ramkumar Ramachandra\n<artagnon@gmail.com> wrote:\n> Felipe Contreras wrote:\n>> +static inline VALUE cstr_to_str(const char *str)\n>> +{\n>> +       if (str == NULL)\n>> +               return Qnil;\n>> +       return rb_str_new2(str);\n>> +}\n>> +\n>> +static VALUE git_rb_setup_git_directory(VALUE self)\n>> +{\n>> +       int nongit_ok;\n>> +       const char *prefix;\n>> +       prefix = setup_git_directory_gently(&nongit_ok);\n>> +       return rb_ary_new3(2, cstr_to_str(prefix), INT2FIX(nongit_ok));\n>> +}\n>\n> Most excellent. Goes to show how well ruby.h is written.\n\nIndeed. I find it easier than using GLib.\n\n> I'm not very familiar with the interface, and am referring to\n> http://www.ruby-doc.org/docs/ProgrammingRuby/html/ext_ruby.html -- let\n> me know if there are some other sources.\n\nThat one is really good and got me almost as far as I am.\n\nOthers might give a bit more information, but nothing too critical.\n\nhttp://stackoverflow.com/tags/ruby-c-api/info\n\n-- \nFelipe Contreras\n"},{"id":"228449","messageId":"CALkWK0nHYt-=gCCavo4tgbgcQFgideDokKakyHxH3VJJtodOnA@mail.gmail.com","threadId":"35044","inReplyTo":"1380405849-13000-45-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 44/44] request-pull: rewrite to C","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-09-29T06:05:33Z","receivedAt":"2013-09-29T06:05:33Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Felipe Contreras wrote:\n>  builtin/request-pull.c | 275 +++++++++++++++++++++++++++++++++++++++++++++++++\n>  git-request-pull.rb    | 199 -----------------------------------\n\nMy head started hurting beyond part 15: I need more coffee ;)\n\nRight now, I'm looking at these two side by side:\nhttps://github.com/felipec/git/blob/724e759e777e21ddc1e1a77b5bad16a301dffe38/git-request-pull.rb\nhttps://github.com/felipec/git/blob/68dd43654e265d8ebe42daf43646c636d3f0ff99/builtin/request-pull.c\n\nTruly beautiful. No more\nkitchen-sink-dependency-embedded-lua-language-war-flame fud.\n\nI look forward to more eyes, and a clean merge. Part 2 is missing a\ncommit message, but the rest of your series looks very reasonable.\nThanks a lot for doing this! You've opened the door for an incredible\namount of learning opportunities.\n"}]}