{"thread":{"id":"4607","subject":"[PATCH] [RFC] Introduce Git.pm","startedAt":"2006-06-22T00:35:46Z","lastAt":"2006-06-22T19:37:48Z","messageCount":7,"participants":["Petr Baudis","Junio C Hamano","Eric Wong","Alex Riesen","Jakub Narebski"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"22229","messageId":"20060622003546.17760.23089.stgit@machine.or.cz","threadId":"4607","inReplyTo":null,"subject":"[PATCH] [RFC] Introduce Git.pm","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-06-22T00:35:46Z","receivedAt":"2006-06-22T00:35:46Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"This patch introduces a very basic and barebone Git.pm module\nwith a sketch of how the generic interface would look like;\nmost functions are missing, but this should give some good base.\nI will expand it in the next days.\n\nMost desirable now is proper error reporting, generic_in() for feeding\ninput to Git commands and the repository() constructor doing some poking\nwith git-rev-parse to get the git directory and subdirectory prefix.\nThose three are basically the prerequisities for converting git-mv.\n\nCurrently Git.pm just wraps up exec()s of Git commands, but even that\nis not trivial to get right and various Git perl scripts do it in\nvarious inconsistent ways. And things will get much more interesting\nwhen we get to XS-izing libgit.\n\nThis adds the Git.pm, integrates it to the build system and as an example\nconverts the git-fmt-merge-msg.perl script to it (the result is not very\nimpressive since its advantage is not quite apparent in this one, but I\njust picked up the simplest Git user around).\n\nThis is mostly to show up what I have so far and gather some comments\non the general shapes of the interface; I guess in the current shape\nof the API it's suitable for pu at best, but that's not my call.\n\nMy current working state is available all the time at\n\n\thttp://pasky.or.cz/~xpasky/git-perl/Git.pm\n\nand an irregularily updated API documentation is at\n\n\thttp://pasky.or.cz/~xpasky/git-perl/Git.html\n\nSigned-off-by: Petr Baudis <pasky@suse.cz>\n---\n\n Makefile               |   13 ++\n git-fmt-merge-msg.perl |    9 +-\n perl/Git.pm            |  284 ++++++++++++++++++++++++++++++++++++++++++++++++\n perl/Makefile.PL       |   14 ++\n 4 files changed, 314 insertions(+), 6 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 0887945..59eca6d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -479,7 +479,8 @@ ### Build rules\n \n all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk\n \n-all:\n+all: perl/Makefile\n+\t$(MAKE) -C perl\n \t$(MAKE) -C templates\n \n strip: $(PROGRAMS) git$X\n@@ -511,7 +512,7 @@ common-cmds.h: Documentation/git-*.txt\n \n $(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl\n \trm -f $@ $@+\n-\tsed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \\\n+\tsed -e '1s|#!.*perl\\(.*\\)|#!$(PERL_PATH_SQ)\\1 -I'\"$$(make -s -C perl instlibdir)\"'|' \\\n \t    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \\\n \t    $@.perl >$@+\n \tchmod +x $@+\n@@ -599,6 +600,9 @@ XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare\n \trm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS)\n \n \n+perl/Makefile:\tperl/Git.pm perl/Makefile.PL\n+\t(cd perl && $(PERL_PATH) Makefile.PL PREFIX=\"$(prefix)\")\n+\n doc:\n \t$(MAKE) -C Documentation all\n \n@@ -618,6 +622,7 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS\n \t@FLAGS='$(TRACK_CFLAGS)'; \\\n \t    if test x\"$$FLAGS\" != x\"`cat GIT-CFLAGS 2>/dev/null`\" ; then \\\n \t\techo 1>&2 \"    * new build flags or prefix\"; \\\n+\t\techo 1>&2 \"    * $$FLAGS != `cat GIT-CFLAGS 2>/dev/null`\"; \\\n \t\techo \"$$FLAGS\" >GIT-CFLAGS; \\\n             fi\n \n@@ -654,6 +659,7 @@ install: all\n \t$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'\n \t$(INSTALL) git$X gitk '$(DESTDIR_SQ)$(bindir_SQ)'\n \t$(MAKE) -C templates install\n+\t$(MAKE) -C perl install\n \t$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'\n \t$(INSTALL) $(PYMODULES) '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'\n \tif test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \\\n@@ -721,7 +727,8 @@ clean:\n \trm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz\n \trm -f $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n-\t$(MAKE) -C templates clean\n+\t[ ! -e perl/Makefile ] || $(MAKE) -C perl/ clean\n+\t$(MAKE) -C templates/ clean\n \t$(MAKE) -C t/ clean\n \trm -f GIT-VERSION-FILE GIT-CFLAGS\n \ndiff --git a/git-fmt-merge-msg.perl b/git-fmt-merge-msg.perl\nindex 5986e54..a2da46e 100755\n--- a/git-fmt-merge-msg.perl\n+++ b/git-fmt-merge-msg.perl\n@@ -6,6 +6,9 @@ # Read .git/FETCH_HEAD and make a human \n # by grouping branches and tags together to form a single line.\n \n use strict;\n+use Git;\n+\n+my $repo = Git->repository();\n \n my @src;\n my %src;\n@@ -28,12 +31,12 @@ sub andjoin {\n }\n \n sub repoconfig {\n-\tmy ($val) = qx{git-repo-config --get merge.summary};\n+\tmy ($val) = $repo->generic_oneline('repo-config', '--get', 'merge.summary');\n \treturn $val;\n }\n \n sub current_branch {\n-\tmy ($bra) = qx{git-symbolic-ref HEAD};\n+\tmy ($bra) = $repo->generic_oneline('symbolic-ref', 'HEAD');\n \tchomp($bra);\n \t$bra =~ s|^refs/heads/||;\n \tif ($bra ne 'master') {\n@@ -47,7 +50,7 @@ sub current_branch {\n sub shortlog {\n \tmy ($tip) = @_;\n \tmy @result;\n-\tforeach ( qx{git-log --no-merges --topo-order --pretty=oneline $tip ^HEAD} ) {\n+\tforeach ($repo->generic('log', '--no-merges', '--topo-order', '--pretty=oneline', $tip, '^HEAD')) {\n \t\ts/^[0-9a-f]{40}\\s+//;\n \t\tpush @result, $_;\n \t}\ndiff --git a/perl/Git.pm b/perl/Git.pm\nnew file mode 100644\nindex 0000000..2f968c5\n--- /dev/null\n+++ b/perl/Git.pm\n@@ -0,0 +1,284 @@\n+=head1 NAME\n+\n+Git - Perl interface to the Git version control system\n+\n+=cut\n+\n+\n+package Git;\n+\n+use strict;\n+\n+use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK);\n+\n+# Totally unstable API.\n+$VERSION = \"0.0.1\";\n+\n+\n+=head1 SYNOPSIS\n+\n+  use Git;\n+\n+  my $version = Git::generic_oneval('version');\n+\n+  Git::generic_vocal('update-server-info');\n+\n+  my $repo = Git->repository (Directory => '/srv/git/cogito.git');\n+\n+\n+  my @revs = $repo->generic('rev-list', '--since=last monday', '--all');\n+\n+  my $fh = $repo->generic('rev-list', '--since=last monday', '--all');\n+  my $lastrev = <$fh>; chomp $lastrev;\n+  close $fh;\n+\n+  my $lastrev = $repo->generic_oneval('rev-list', '--all');\n+\n+=cut\n+\n+\n+require Exporter;\n+\n+@ISA = qw(Exporter);\n+\n+@EXPORT = qw();\n+\n+# Methods which can be called as standalone functions as well:\n+@EXPORT_OK = qw(generic generic_oneval generic_vocal);\n+\n+\n+=head1 DESCRIPTION\n+\n+This module provides Perl scripts easy way to interface the Git version control\n+system. The modules have an easy and well-tested way to call arbitrary Git\n+commands; in the future, the interface will also provide specialized methods\n+for doing easily operations which are not totally trivial to do over the generic\n+interface.\n+\n+While some commands can be executed outside of any context (e.g. 'version'\n+or 'init-db'), most operations require a repository context, which in practice\n+means getting an instance of the Git object using the repository() constructor.\n+(In the future, we will also get a new_repository() constructor.) All commands\n+called as methods of the object are then executed in the context of the\n+repository.\n+\n+TODO: In the future, we might also do\n+\n+\tmy $subdir = $repo->subdir('Documentation');\n+\t# Gets called in the subdirectory context:\n+\t$subdir->generic('status');\n+\n+\tmy $remoterepo = $repo->remote_repository (name => 'cogito', branch => 'master');\n+\t$remoterepo ||= Git->remote_repository ('http://git.or.cz/cogito.git/');\n+\tmy @refs = $remoterepo->refs();\n+\n+So far, all functions just die if anything goes wrong. If you don't want that,\n+make appropriate provisions to catch the possible deaths. Better error recovery\n+mechanisms will be provided in the future.\n+\n+Currently, the module merely wraps calls to external Git tools. In the future,\n+it will provide a much faster way to interact with Git by linking directly\n+to libgit. This should be completely opaque to the user, though (performance\n+increate nonwithstanding).\n+\n+=cut\n+\n+\n+use IPC::Open3;\n+\n+\n+=head1 CONSTRUCTORS\n+\n+=over 4\n+\n+=item repository ( OPTIONS )\n+\n+=item repository ( DIRECTORY )\n+\n+=item repository ()\n+\n+Construct a new repository object.\n+C<OPTIONS> are passed in a hash like fashion, using key and value pairs.\n+Possible options are:\n+\n+B<Repository> - Path to the Git repository.\n+\n+B<WorkingCopy> - Path to the associated working copy; not strictly required\n+as many commands will happily crunch on a bare repository.\n+\n+B<Directory> - Path to the Git working directory in its usual setup. This\n+is just for convenient setting of both C<Repository> and C<WorkingCopy>\n+at once: If the directory as a C<.git> subdirectory, C<Repository> is pointed\n+to the subdirectory and the directory is assumed to be the working copy.\n+If the directory does not have the subdirectory, C<WorkingCopy> is left\n+undefined and C<Repository> is pointed to the directory itself.\n+\n+You should not use both C<Directory> and either of C<Repository> and\n+C<WorkingCopy> - the results of that are undefined.\n+\n+Alternatively, a directory path may be passed as a single scalar argument\n+to the constructor; it is equivalent to setting only the C<Directory> option\n+field.\n+\n+Calling the constructor with no options whatsoever is equivalent to\n+calling it with C<< Directory => '.' >>.\n+\n+=cut\n+\n+sub repository {\n+\tmy $class = shift;\n+\tmy @args = @_;\n+\tmy %opts = ();\n+\tmy $self;\n+\n+\tif (defined $args[0]) {\n+\t\tif ($#args % 2 != 1) {\n+\t\t\t# Not a hash.\n+\t\t\t$#args == 0 or die \"Git::repository(): bad usage\";\n+\t\t\t%opts = (Directory => $args[0]);\n+\t\t} else {\n+\t\t\t%opts = @args;\n+\t\t}\n+\n+\t\tif ($opts{Directory}) {\n+\t\t\t-d $opts{Directory} or die \"Git::repository($opts{Directory}): $!\";\n+\t\t\tif (-d $opts{Directory}.\"/.git\") {\n+\t\t\t\t# TODO: Might make this more clever\n+\t\t\t\t$opts{WorkingCopy} = $opts{Directory};\n+\t\t\t\t$opts{Repository} = $opts{Directory}.\"/.git\";\n+\t\t\t} else {\n+\t\t\t\t$opts{Repository} = $opts{Directory};\n+\t\t\t}\n+\t\t\tdelete $opts{Directory};\n+\t\t}\n+\t}\n+\n+\t$self = { opts => \\%opts };\n+\tbless $self, $class;\n+}\n+\n+\n+=back\n+\n+=head1 METHODS\n+\n+=over 4\n+\n+=item generic ( COMMAND [, ARGUMENTS... ] )\n+\n+Execute the given Git C<COMMAND> (specify it without the 'git-'\n+prefix), optionally with the specified extra C<ARGUMENTS>. The method can be\n+called without any instance or on a specified Git repository.\n+\n+In scalar context, it returns a filehandle containing the command output.\n+\n+In array context, it returns an array containing lines printed to the\n+command's stdout.\n+\n+In both cases, the command's stdin and stderr are the same as the caller's.\n+\n+=cut\n+\n+sub generic {\n+\tmy ($self, $cmd, @args) = _maybe_self(@_);\n+\n+\t$cmd =~ /^[a-z0-9A-Z_-]+$/ or die \"bad command: $cmd\";\n+\n+\tmy $pid = open(my $fh, \"-|\");\n+\tif ($pid < 0) {\n+\t\tdie \"generic($cmd, @args) open: $!\";\n+\t} elsif ($pid == 0) {\n+\t\t_do_exec($self, $cmd, @args);\n+\t}\n+\n+\treturn $fh unless wantarray;\n+\n+\tmy @lines = <$fh>;\n+\tclose $fh;\n+\tchomp @lines;\n+\treturn @lines;\n+}\n+\n+=item generic_oneline ( COMMAND [, ARGUMENTS... ] )\n+\n+Execute the given C<COMMAND> in the same way as generic()\n+does but always return a scalar string containing the first line\n+of the command's standard output.\n+\n+=cut\n+\n+sub generic_oneline {\n+\tmy $fh = generic(@_);\n+\tmy $line = <$fh>;\n+\tclose $fh;\n+\tchomp $line;\n+\treturn $line;\n+}\n+\n+=item generic_vocal ( COMMAND [, ARGUMENTS... ] )\n+\n+Execute the given C<COMMAND> in the same way as generic() does but does not\n+capture the command output - the standard output is not redirected and goes\n+to the standard output of the caller application.\n+\n+While the method is called generic_vocal(), you might want to as well use\n+it for the most silent Git commands which you know will never pollute your\n+stdout but you want to avoid the overhead of the pipe setup when calling them.\n+\n+The function returns only after the command has finished running.\n+\n+=cut\n+\n+sub generic_vocal {\n+\tmy ($self, $cmd, @args) = _maybe_self(@_);\n+\n+\t$cmd =~ /^[a-z0-9A-Z_-]+$/ or die \"bad command: $cmd\";\n+\n+\tmy $pid = fork;\n+\tif ($pid < 0) {\n+\t\tdie \"generic_vocal($cmd, @args) fork: $!\";\n+\t} elsif ($pid == 0) {\n+\t\t_do_exec($self, $cmd, @args);\n+\t}\n+\twaitpid($pid, 0);\n+}\n+\n+\n+=back\n+\n+=head1 TODO\n+\n+This is still fairly crude. Repository objects not yet supported.\n+We need some good way to report errors back except just dying.\n+\n+=head1 COPYRIGHT\n+\n+Copyright 2006 by Petr Baudis E<lt>pasky@suse.czE<gt>.\n+\n+This module is free software; it may be used, copied, modified\n+and distributed under the terms of the GNU General Public Licence,\n+either version 2, or (at your option) any later version.\n+\n+=cut\n+\n+\n+# Takes raw method argument list and returns ($obj, @args) in case\n+# the method was called upon an instance and (undef, @args) if\n+# it was called directly.\n+sub _maybe_self {\n+\t# This breaks inheritance. Oh well.\n+\tref $_[0] eq 'Git' ? @_ : (undef, @_);\n+}\n+\n+# When already in the subprocess, set up the appropriate state\n+# for the given repository and execute the git command.\n+sub _do_exec {\n+\tmy ($self, @args) = @_;\n+\tif ($self) {\n+\t\t$self->{opts}->{Repository} and $ENV{'GIT_DIR'} = $self->{opts}->{Repository};\n+\t\t$self->{opts}->{WorkingCopy} and chdir($self->{opts}->{WorkingCopy});\n+\t}\n+\texec ('git', @args) or die \"Git exec: $!\";\n+}\n+\n+1; # Famous final words\ndiff --git a/perl/Makefile.PL b/perl/Makefile.PL\nnew file mode 100644\nindex 0000000..703ea98\n--- /dev/null\n+++ b/perl/Makefile.PL\n@@ -0,0 +1,14 @@\n+use ExtUtils::MakeMaker;\n+\n+sub MY::postamble {\n+\treturn <<'MAKE_FRAG';\n+instlibdir:\n+\t@echo $(INSTALLSITELIB)\n+\n+MAKE_FRAG\n+}\n+\n+WriteMakefile(\n+\tNAME            => 'Git',\n+\tVERSION_FROM    => 'Git.pm'\n+);\n"},{"id":"22230","messageId":"7v64iuxard.fsf@assigned-by-dhcp.cox.net","threadId":"4607","inReplyTo":"20060622003546.17760.23089.stgit@machine.or.cz","subject":"Re: [PATCH] [RFC] Introduce Git.pm","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-06-22T01:03:02Z","receivedAt":"2006-06-22T01:03:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Petr Baudis <pasky@suse.cz> writes:\n\n> Currently Git.pm just wraps up exec()s of Git commands, but even that\n> is not trivial to get right and various Git perl scripts do it in\n> various inconsistent ways. And things will get much more interesting\n> when we get to XS-izing libgit.\n\nWe would probably need some moderate to major surgery before\ndoing that depending on which parts.  Worrisome are object layer\nthat holds onto parsed objects and keeps flag bits from the\nprevious runs, and lockfile that are cleaned up via atexit() --\nthere may be others.\n\n> @@ -618,6 +622,7 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS\n>  \t@FLAGS='$(TRACK_CFLAGS)'; \\\n>  \t    if test x\"$$FLAGS\" != x\"`cat GIT-CFLAGS 2>/dev/null`\" ; then \\\n>  \t\techo 1>&2 \"    * new build flags or prefix\"; \\\n> +\t\techo 1>&2 \"    * $$FLAGS != `cat GIT-CFLAGS 2>/dev/null`\"; \\\n>  \t\techo \"$$FLAGS\" >GIT-CFLAGS; \\\n>              fi\n>  \n\nOfftopic; do people mind if we drop $GIT_VERSION from\nTRACK_CFLAGS?  Every time I switch branches or make a new commit\nit ends up rebuilding everything needlessly.\n\n>  sub current_branch {\n> -\tmy ($bra) = qx{git-symbolic-ref HEAD};\n> +\tmy ($bra) = $repo->generic_oneline('symbolic-ref', 'HEAD');\n>  \tchomp($bra);\n>  \t$bra =~ s|^refs/heads/||;\n>  \tif ($bra ne 'master') {\n\nYour *_oneline chomps so chomp($bra) is not needed anymore, I think.\n\nIn any case, this is an excellent move to clean things up.\nVarious implementations of qx{} will be all in one place so\nActiveState folks would have easier time adjusting things to\ntheir environment.\n\n> +require Exporter;\n\nNot \"use\"?\n\n> +# Methods which can be called as standalone functions as well:\n> +@EXPORT_OK = qw(generic generic_oneval generic_vocal);\n\nI am not sure generic_xxx is a good name.  Perhaps command_xxx?\n\nAlso as you say in the comment, \"vocal\" sounds a bit funny.\nI wonder if we can use wantarray to detect the case where the\ncaller wants _no_ value, and do the \"not piping\" optimization in\nthat case.\n"},{"id":"22232","messageId":"20060622023104.GB27871@hand.yhbt.net","threadId":"4607","inReplyTo":"20060622003546.17760.23089.stgit@machine.or.cz","subject":"Re: [PATCH] [RFC] Introduce Git.pm","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-06-22T02:31:05Z","receivedAt":"2006-06-22T02:31:05Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Petr Baudis <pasky@suse.cz> wrote:\n> Currently Git.pm just wraps up exec()s of Git commands, but even that\n> is not trivial to get right and various Git perl scripts do it in\n> various inconsistent ways. And things will get much more interesting\n> when we get to XS-izing libgit.\n\ngit-svn uses fork/pipe/exec very heavily for both reads and writes and\nalso a few bidirectional cases, as well.  It would be good exercise to\nport it to use Git.pm once it's more complete and stable.\n\nXS-ising libgit would be awesome with all the git-hash-object calls in\ngit-svn (and other importers, too, I imagine).\n\n> This is mostly to show up what I have so far and gather some comments\n> on the general shapes of the interface; I guess in the current shape\n> of the API it's suitable for pu at best, but that's not my call.\n\nsafe_qx (git-svn) AKA safe_pipe_capture (archimport) would be\nnon-git-specific, but very useful.\n\n> +\tmy $pid = open(my $fh, \"-|\");\n> +\tif ($pid < 0) {\n> +\t\tdie \"generic($cmd, @args) open: $!\";\n\nIn Perl, fork returns undef instead of $pid < 0 on failure.  Doing\njoin(' ',@args) will also make the error message more readable, too :)\n\n-- \nEric Wong\n"},{"id":"22240","messageId":"81b0412b0606220018h5a67b45dk1aa320e24fc172bc@mail.gmail.com","threadId":"4607","inReplyTo":"7v64iuxard.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] [RFC] Introduce Git.pm","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2006-06-22T07:18:12Z","receivedAt":"2006-06-22T07:18:12Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"On 6/22/06, Junio C Hamano <junkio@cox.net> wrote:\n> Offtopic; do people mind if we drop $GIT_VERSION from\n> TRACK_CFLAGS?  Every time I switch branches or make a new commit\n> it ends up rebuilding everything needlessly.\n\nMe too. That's one of the you're too lazy to report, because you\nthink: \"it is not yet annoying enough\".\n"},{"id":"22245","messageId":"e7dj4h$cv7$1@sea.gmane.org","threadId":"4607","inReplyTo":"20060622003546.17760.23089.stgit@machine.or.cz","subject":"Re: [PATCH] [RFC] Introduce Git.pm","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-06-22T08:07:58Z","receivedAt":"2006-06-22T08:07:58Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Petr Baudis wrote:\n\n> Most desirable now is proper error reporting, generic_in() for feeding\n> input to Git commands and the repository() constructor doing some poking\n> with git-rev-parse to get the git directory and subdirectory prefix.\n> Those three are basically the prerequisities for converting git-mv.\n\nI think that repository() constructor (to be converted in the 'new'\nconstructor I guess) should take 'binbin => \"/usr/bin\"' argument\noptionally, to find where git binary is. 'libexecdir' can be also\nsupported, or one can get it from 'git --exec-path' and save.\n \n> Currently Git.pm just wraps up exec()s of Git commands, but even that\n> is not trivial to get right and various Git perl scripts do it in\n> various inconsistent ways.\n\nIt would be nice to have also wrapers to reading files from git \nrepository, ate least some generic read_file/readdir.\n\nFor example it might be faster to list branches and/or tags by directly\nreading $GIT_DIR/refs directory than using 'git branch' or 'git tag -l'.\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"22250","messageId":"7vfyhxtvgl.fsf@assigned-by-dhcp.cox.net","threadId":"4607","inReplyTo":"20060622003546.17760.23089.stgit@machine.or.cz","subject":"Re: [PATCH] [RFC] Introduce Git.pm","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-06-22T09:01:46Z","receivedAt":"2006-06-22T09:01:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Petr Baudis <pasky@suse.cz> writes:\n\n> This patch introduces a very basic and barebone Git.pm module\n> with a sketch of how the generic interface would look like;\n\nIn addition to my previous comments...\n\n> +  my $version = Git::generic_oneval('version');\n> +  my $lastrev = $repo->generic_oneval('rev-list', '--all');\n> +...\n> +=item generic_oneline ( COMMAND [, ARGUMENTS... ] )\n> +...\n> +sub generic_oneline {\n> +\tmy $fh = generic(@_);\n> +\tmy $line = <$fh>;\n> +\tclose $fh;\n> +\tchomp $line;\n> +\treturn $line;\n> +}\n\nLet's have a cleaned up version of Git.pm, Makefile changes and\nchanges to git-fmt-merge-msg.perl that shows how to use the\nmodule.  Being in-tree would help people to view, use and\ncomment on it.\n"},{"id":"22296","messageId":"20060622193748.GD21864@pasky.or.cz","threadId":"4607","inReplyTo":"20060622023104.GB27871@hand.yhbt.net","subject":"Re: [PATCH] [RFC] Introduce Git.pm","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-06-22T19:37:48Z","receivedAt":"2006-06-22T19:37:48Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Dear diary, on Thu, Jun 22, 2006 at 03:03:02AM CEST, I got a letter\nwhere Junio C Hamano <junkio@cox.net> said that...\n> Petr Baudis <pasky@suse.cz> writes:\n> \n> > Currently Git.pm just wraps up exec()s of Git commands, but even that\n> > is not trivial to get right and various Git perl scripts do it in\n> > various inconsistent ways. And things will get much more interesting\n> > when we get to XS-izing libgit.\n> \n> We would probably need some moderate to major surgery before\n> doing that depending on which parts.  Worrisome are object layer\n> that holds onto parsed objects and keeps flag bits from the\n> previous runs, and lockfile that are cleaned up via atexit() --\n> there may be others.\n\nYes, I'm aware of that. Still, some parts should be quite easy...\n\nDear diary, on Thu, Jun 22, 2006 at 04:31:05AM CEST, I got a letter\nwhere Eric Wong <normalperson@yhbt.net> said that...\n> XS-ising libgit would be awesome with all the git-hash-object calls in\n> git-svn (and other importers, too, I imagine).\n\n...so I couldn't resist that and did add hash_object() xs-ized\nfunction. Looks pretty sweet:\n\n$ time perl -w -I/home/xpasky/lib/perl5/site_perl/5.8.8 -MGit -le 'print Git::generic_oneline(\"hash-object\", \"Makefile\") for (1..1000)' >/dev/null\n\nreal    0m4.800s\nuser    0m2.072s\nsys     0m2.560s\n\n$ time perl -w -I/home/xpasky/lib/perl5/site_perl/5.8.8 -MGit -le 'print Git::hash_object(\"Makefile\") for (1..1000)' >/dev/null\n\nreal    0m0.179s\nuser    0m0.144s\nsys     0m0.032s\n\n\nDear diary, on Thu, Jun 22, 2006 at 03:03:02AM CEST, I got a letter\nwhere Junio C Hamano <junkio@cox.net> said that...\n> > @@ -618,6 +622,7 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS\n> >  \t@FLAGS='$(TRACK_CFLAGS)'; \\\n> >  \t    if test x\"$$FLAGS\" != x\"`cat GIT-CFLAGS 2>/dev/null`\" ; then \\\n> >  \t\techo 1>&2 \"    * new build flags or prefix\"; \\\n> > +\t\techo 1>&2 \"    * $$FLAGS != `cat GIT-CFLAGS 2>/dev/null`\"; \\\n> >  \t\techo \"$$FLAGS\" >GIT-CFLAGS; \\\n> >              fi\n> >  \n> \n> Offtopic; do people mind if we drop $GIT_VERSION from\n> TRACK_CFLAGS?  Every time I switch branches or make a new commit\n> it ends up rebuilding everything needlessly.\n\nPlease, please, _do_! :-)\n\nI'm sorry about this hunk, I added it to just figure out why the heck it\nkeeps rebuilding everything from scratch and forgot to drop it.\n\n> >  sub current_branch {\n> > -\tmy ($bra) = qx{git-symbolic-ref HEAD};\n> > +\tmy ($bra) = $repo->generic_oneline('symbolic-ref', 'HEAD');\n> >  \tchomp($bra);\n> >  \t$bra =~ s|^refs/heads/||;\n> >  \tif ($bra ne 'master') {\n> \n> Your *_oneline chomps so chomp($bra) is not needed anymore, I think.\n\nAh, thanks.\n\n> > +require Exporter;\n> \n> Not \"use\"?\n\nThe idiom is to require, I guess; we don't need to import anything.\nI don't think it matters and I'm not entirely sure about all the\nimplications of the difference between the two, so if you wish,\nI can change it.\n\n> > +# Methods which can be called as standalone functions as well:\n> > +@EXPORT_OK = qw(generic generic_oneval generic_vocal);\n> \n> I am not sure generic_xxx is a good name.  Perhaps command_xxx?\n\nTechnically, other methods can be commands, too. ;-) But yet, it sounds\nbetter. I've changed it.\n\n> Also as you say in the comment, \"vocal\" sounds a bit funny.\n> I wonder if we can use wantarray to detect the case where the\n> caller wants _no_ value, and do the \"not piping\" optimization in\n> that case.\n\nI think that would be rather confusing since generic_vocal does not make\nthe output go away but makes it go to stdout, while if you just throw\naway return value of generic(), you expect it to go nowhere.\n\nAt least I will change 'vocal' to 'noisy' so that it does not sound\nso weird.\n\n\nDear diary, on Thu, Jun 22, 2006 at 04:31:05AM CEST, I got a letter\nwhere Eric Wong <normalperson@yhbt.net> said that...\n> Petr Baudis <pasky@suse.cz> wrote:\n> > +\tmy $pid = open(my $fh, \"-|\");\n> > +\tif ($pid < 0) {\n> > +\t\tdie \"generic($cmd, @args) open: $!\";\n> \n> In Perl, fork returns undef instead of $pid < 0 on failure.\n\nThanks!\n\n> Doing join(' ',@args) will also make the error message more readable, too :)\n\n@args should get automagically interpolated by having the members\nseparated by $\" (space by default).\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\nA person is just about as big as the things that make them angry.\n"}]}