On Fri, Jun 7, 2013 at 9:17 PM, Duy Nguyen <pclo...@gmail.com> wrote:
> On Thu, Jun 6, 2013 at 11:22 PM, Johannes Schindelin
> <johannes.schinde...@gmx.de> wrote:
>> Hi Greg,
>> On Thu, 6 Jun 2013, Greg Troxel wrote:
>>> As one of the people who helps maintain git packages in pkgsrc, my
>>> initial reaction is negative to adding a ruby dependency.
>> My initial reaction, too. It was hard enough to get Perl included with Git
>> for Windows (because of that pesky Subversion dependency).
>> As you can see from the commit history, I was the primary force behind
>> trying to get everything "core" in Git away from requiring scripting
>> languages (I think it is an awesome thing to provide APIs for as many
>> languages as possible, but a not-so-cool thing to use more than one
>> language in the core code). It does not seem that anybody picked up that
>> task when I left, though.
> Nobody seems to mention it yet. There's another reason behind the C
> rewrite effort: fork is costly on Windows. The C rewrite allows us to
> run with one process (most of the time). This applies for shell, perl
> and even ruby scripts because libgit.a is never meant to be used
> outside git.c context (unlike libgit2). In this regard, ruby is just
> as bad as currently supported non-C languages.

Are you sure?


cat > /tmp/test <<EOF
require './git'

(1..100).each do |e|
  puts \`git rev-parse HEAD~#{e}\`

strace -o /tmp/log -e fork,clone ruby /tmp/test
cat /tmp/log

parent_tidptr=0x7f84131dc9d0, tls=0x7f84131dc700,
child_tidptr=0x7f84131dc9d0) = 17455
+++ exited with 0 +++

I wrote a simple Ruby extension to access Git builtins so `git
rev-parse` actually calls cmd_rev_parse directly. I don't know of any
other language that supports so much extensibility. Of course, as soon
as one command does exit(), the script ends too. It could be useful to
do experiments though.

Felipe Contreras
#include <builtin.h>
#include <cache.h>
#include <fcntl.h>

#undef PATH_SEP

#include <ruby.h>

static VALUE shellwords;

struct cmd_struct {
	const char *cmd;
	int (*fn)(int, const char **, const char *);
	int option;

#define RUN_SETUP (1 << 0)
#define RUN_SETUP_GENTLY (1 << 1)
#define USE_PAGER (1 << 2)
#define NEED_WORK_TREE (1 << 3)

static struct cmd_struct commands[] = {
	{ "rev-parse", cmd_rev_parse },
	{ "show", cmd_show, RUN_SETUP },

static VALUE git_rb_backticks(int o_argc, VALUE *o_argv, VALUE ctx)
	int argc, i, old;
	int pipefd[2];
	const char **argv;
	char buf[0x1000];
	VALUE command;
	int do_read;
	struct cmd_struct *cmd = NULL;
	const char *prefix = NULL;

	if (strncmp(RSTRING_PTR(o_argv[0]), "git ", 4)) {
		VALUE port, result;
		port = rb_funcall(rb_cIO, rb_intern("popen"), 1, o_argv[0]);
		result = rb_funcall(port, rb_intern("read"), 0);
		rb_funcall(result, rb_intern("chomp!"), 0);
		return result;

	command = rb_funcall(shellwords, rb_intern("shellsplit"), 1, o_argv[0]);

	argc = RARRAY_LEN(command);
	argv = xcalloc(sizeof(*argv), argc);

	VALUE *rarray = RARRAY_PTR(command);
	for (i = 0; i < argc; i++)
		argv[i] = RSTRING_PTR(rarray[i]);

	old = dup(1);
	i = pipe(pipefd);
	dup2(pipefd[1], 1);

	for (i = 0; i < ARRAY_SIZE(commands); i++) {
		struct cmd_struct *p = &commands[i];
		if (strcmp(p->cmd, argv[0]))
		cmd = p;

	if (!cmd)
		rb_raise(rb_eArgError, "unknown command: %s", argv[0]);

	if (cmd->option & RUN_SETUP)
		prefix = setup_git_directory();

	i = cmd->fn(argc, argv, prefix);
	rb_last_status_set(i, getpid());

	dup2(old, 1);

	i = read(pipefd[0], buf, sizeof(buf));
	if (buf[i - 1] == '\n')
		i -= 1;
	buf[i] = '\0';

	return rb_str_new(buf, i);

void Init_git(void)
	shellwords = rb_define_module("Shellwords");
	rb_define_global_function("`", git_rb_backticks, -1);

Reply via email to