Subject: [RFC] Git Perl bindings, and OO interface

From: Jakub Narebski <[email-masked]>
Date: Thu, 27 Nov 2008 03:58:49 +0200

There exists many Git bindings for various programming languages, some 
of them using git commands, some of them reimplementing Git, or parts 
of Git.  There is GitPython and PyGit (with some native implementation)
for Python, there is (deprecated) Ruby/Git and Grit (with some native
implementation) for Ruby, there is #Git for C#, there is ObjectiveGit
for Objective C (native), there is JGit (native) and JavaGit for Java,
there is Gat for Haskell and eWiki contains something for PHP.

And of course there is Git.pm (included with Git) and Git::Repo (part
of 'gitweb caching' GSoC 2008 project by Lea Wiemann) for Perl.  Now
Git::Repo didn't get accepted into git.git codebase, but developing it
sparked a bit of discussion about Perl interface to Git commands, and
Object Oriented interface to Git.

I'd like to spawn a discussion in this thread about interfaces to Git
and Object Oriented interface to Git, mainly but not only in Perl.
I hope that the authors of mentioned (and not mentioned) bindings, 
interfaces and implementations of Git would contribute to this thread.

0. One of points of disagreement between Git.pm and new Git::Repo was
   using Error module for frontend error handling.  While the 
   explanation in http://www.perl.com/pub/a/2002/11/14/exception.html
   is compelling, it is not standard Perl technique.  Additionally
   adding "cmd_git_try { CODE } ERRORMSG" syntactic sugar was not very
   good idea.

   So the first thing I'd like to discuss: to use Error and try/catch,
   or not in Perl interface (bindings) to Git?  I would really like to
   hear from Perl experts / Perl hackers here...

1. Git::Cmd

   If I remember correctly Git.pm started as a way to gather in one
   place safe_cmd and safe_pipe like construct from various git commands
   implemented in Perl.  The goal here is to provide portable, safe, and
   working with old Perl interface:
    * portable: this means trying to work with ActiveState Perl on 
      MS Windows; I don't know how important it is _now_ (if there are
      common other Perl distributions on MS Windows).
    * safe: if some of arguments to git commands come from variables,
      then they have to be safe against shell expansion (whitespace,
      quoting characters, escape characters, metacharacters, etc.).
    * compatibile: it should work with as old Perl version as is
      reasonable; it is possible that you can install git locally, but
      cannot upgrade Perl.

   Note that some git commands, for example 'git version', 'git
   ls-remote' and 'git clone' doesn't need git repository to work on.

   We would want to be able to catch git command output to scalar, to
   list (line by line), and to filehandle. More advanced stuff is bidi
   pipe (watch for deadlocks!), and redirecting both stdout and stderr
   of git command to filehandle.

   What instance of Git::Cmd should know is where to find 'git' binary
   (what is $GIT in gitweb, for example). It could cache/store
   internally exec_path.

2. Git::Config

   If git command (a piece of code) uses more than one configuration
   variable, then one would want to get relevant configuration using
   as few calls to git commands as possible.  Therefore using git-config
   to read each config variable is usually out of the question (but it
   is sometimes useful); we would want to read all config in one go,
   either by using "git config -l -z", or by writing config parser in
   Perl (as some command(s) did).

   The problem with this solution is that we have to implement "type
   casting", i.e. equivalent of --int and --bool options to git-config
   ourselves. This mean converting to integer with optional size suffix,
   converting to boolean, and asking for escape codes corresponding to
   given color. And if we add new type (like proposed --path, expanding
   for example '~' to HOME, and ~user to home directory of given user)
   we would have to add it to Perl interface too.
  
3. Git::Repo::Bare and Git::Repo::Nonbare

   Git.pm partially implements those, in a kind of mixed way. Git::Repo
   from Lea Wiemann implements if I remember correctly bare repo only.

   What Git::Repo::Bare (or just Git::Repo) should support is to pass
   appropriate '--git-dir=<dir>' to Git::Cmd, and support accessing git
   repository config via Git::Config.  It could have also use
   long-running pipe to "git cat-file --batch / --batch-check"
   invocation.  For gitweb we only need that part.

   Git::Repo::Nonbare has to additionally pass '--work-tree=<dir>' if
   needed, ant be able to take care and manipulate where in working
   directory we are, i.e. what for example "git rev-parse --show-prefix"
   does.

4. Git::Object: Git::Commit, Git::Tag, Git::Blob and Git::Tree

   Here begins "true" object-oriented part of Git Perl API.

   The easy part is for Git::Commit and Git::Tag to parse commit and tag
   objects (perhaps Git::Object should have interface for long-lived
   "git cat-file --batch") into headers and body (commit/tag message).
   I think we can borrow / be inspired by parse_commit() and other such
   code in gitweb; we have to remember that there might be in some time
   some new headers we don't know about but are perfectly valid (see for
   example "encoding" header in commit object format, which was added
   later, not during initial design).

   The harder part would be to be able to deal with author and committer
   info, splitting it into parts (author name, author email, date and
   timezone, etc.), and also generating dates in various formats, like
   RFC-2822 or ISO-8601.

   The easiest part would be structureless Git::Blob... but there we
   might want size of blob.

   A bit harder would be Git::Tree object and dealing with elements of
   a tree (tree entries).  I'm not sure if some kind of iterator access
   would be useful here.

   Note that for Git::Commit if we are to use plumbing like git-cat-file
   we would have to take care of fake parents info, namely grafts and
   shallow info by ourself, in Perl, to have 'effective parents'.

5. Git::Diff::Raw and Git::Diff::Patchset

   Here I am thinking simply about parsing difftree (raw diff output
   format) and patchset format, as it is used in gitweb.  It is meant
   to be able to access for example to permissions of a file, or diff
   status, or diff stats, etc.

   Here we would want to be able to deal also with merge commits and
   combined diff output format.

6. Git::Log or Git::RevList

   The only difference from list of Git::Commit objects is that 
   depending on parameters like path limiting it might have different
   effective parents if there is history simplification.

7. Git::Refs

   It is meant to represent references, mainly branches, and be filled
   using git-for-each-ref... and for example used for ref markers.

There are probably a few things I have forgot about...
-- 
Jakub Narebski
Poland