It was discussed before that there was a need to replace Git scripts from perl
and sh that utilize the 'git' binary to do everything they need, which requires
many forks, and that creates problems on platforms like Windows.

This is a first step meant to show how a solution using Ruby would look like.

Other alternatives just don't cut it. Shell scripts are too simple, and
invariably require forks. Perl could use Git's internal C code, but it's syntax
is too cumbersome and it's loosing more and more popularity. Python and Ruby
are the only modern languages that could fit all the needs, but Python's syntax
is not ideal, specially considering the background of the Git community, and
also, Ruby's C extensibility is simply superb.

This patch series introduces Ruby bindings for Git's C internal library, and
add example commands to show how it could be used, and how it resembles the
original C code, shell code, and perl code. Basically, Ruby fits like a glove.

== Syntax ==

First of all, the syntax of Ruby is very similar to other languages used by Git:


  if (a && b || (c & FLAG) != 0)
    return 0

  printf("format: %s: %i\n", "this string", 0)

  count += 1


  out = `git rev-parse #{committish}`

  str = <<EOF


  var ||= 'default'

The following Perl code:

  sub abbr {
    my $ref = shift;
    if ($ref =~ m{^refs/heads/(.*)$}) {
      return $1;
    return $ref;

Looks like this in Ruby:

  def abbr(ref)
    if (ref =~ %r{^refs/heads/(.*)$}m)
      return $1
    return ref

== C bindings ==

It's extremely easy to write wrappers for Git's C functions:

  static VALUE git_rb_get_git_dir(VALUE self)
    return rb_str_new2(get_git_dir());

  rb_define_global_function("get_git_dir", git_rb_get_git_dir, 0);

Then in Ruby:

  dir = get_git_dir()

== Much more ==

Ruby's power allows for plenty of extensibility.

For example:

  p `echo yes`
  => "yes\n"

Usually we want to chomp the last new line. Fortunately everything is open in
Ruby, so it's possible to override the `() method:

  def `(cmd)
    IO.popen(cmd) { |pipe| pipe.read.chomp }


  p `echo yes`
  => "yes"

Also, in shell, if we want to make sure a sequence of commands is executed, we
would do something like:

  git command 1 &&
  git command 2 &&
  git command 3 ||

This gets specially troublesome the bigger the sequence. In Ruby:

  def run(*args)
    raise RuntimeError unless $?.success?

    run('git command 1')
    run('git command 2')
    run('git command 3')
  rescue RuntimeError

Finally, Ruby has the concept of blocks:

  def run_with_lock()
    puts "lock"
    puts "unlock"

  branch = 'master'

  run_with_lock() do
    puts "do stuff with #{branch}"

Notice how the block inside run_with_lock() is able to access the 'branch'
variable, because it's in it's context, but the block is not actually called
until run_with_lock() calls 'yield'.

Python has a similar concept, but not nearly as powerful.

== Upgrade path ==

Ruby 2.0 didn't suffer the problems Python 3 is suffering because they have a
sane upgrade path.

Ruby 1.9 changed the syntax, so people using 1.8 had to do minor changes to
upgrade to 1.9, but they didn't miss many major features. After people updated
to 1.9, 2.0 came along with major features, but it was compatible with 1.9, so
nobody had to do any further changes.

Either way, it's still possible to run code that works in 1.8, 1.9, and 2.0.

== Community ==

The Ruby community is already heavily engaged in Git, as statistics in GitHub
show, plenty of Ruby projects use Git, outnumbering by far the Python ones.

There might be some correlation based on the fact that Mercurial is written in


== Conclusion ==

Ruby is an extremly powerful modern language, it features object oriented
paradigm, as well as functional, and procedural. It borrows from languages such
as C, Perl, and many others.

It's possible that by opening the possibilities to write Ruby scripts, many
Ruby developers would join the effort to improve Git.

Felipe Contreras (7):
  Add support for ruby commands
  ruby: add setup script
  ruby: add simple wrappers
  ruby: rewrite 'request-pull'
  ruby: rewrite perl script
  ruby: remove one fork
  ruby: rewrite 'reset'

 Makefile            |  16 +-
 cache.h             |   2 +
 git-rb-setup.rb     | 125 +++++++++++++
 git-refs.rb         |   7 +
 git-request-pull.rb | 159 ++++++++++++++++
 git-request-pull.sh | 162 -----------------
 git-reset.rb        | 223 +++++++++++++++++++++++
 git.c               |   4 +-
 ruby.c              | 511 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 1044 insertions(+), 165 deletions(-)
 create mode 100644 git-rb-setup.rb
 create mode 100644 git-refs.rb
 create mode 100644 git-request-pull.rb
 delete mode 100755 git-request-pull.sh
 create mode 100644 git-reset.rb
 create mode 100644 ruby.c


To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to