{"thread":{"id":"4751","subject":"[PATCH 4/6] Make it possible to set up libgit directly (instead of from the environment)","startedAt":"2006-07-03T20:44:15Z","lastAt":"2006-07-03T21:49:56Z","messageCount":9,"participants":["Petr Baudis"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"23133","messageId":"20060703204415.28541.47920.stgit@machine.or.cz","threadId":"4751","inReplyTo":null,"subject":"[PATCH 0/6] The residual Git.pm patches","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-07-03T20:44:15Z","receivedAt":"2006-07-03T20:44:15Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"  Hi,\n\n  this series is not so much meant for immediate application but\nrather so that you can leave it lingering on top of pu, if you wish,\nand especially of fear of it being lost in all the turmoil around\nGit.pm - this is what was left in my StGIT series after weeding out\nwhat ended up in next, neatly stacked in a single mail thread.\n\n\t\t\t\tPetr Baudis\n\n-- \nAnd on the eigth day, God started debugging.\n"},{"id":"23134","messageId":"20060703204755.28541.48666.stgit@machine.or.cz","threadId":"4751","inReplyTo":"20060703204415.28541.47920.stgit@machine.or.cz","subject":"[PATCH 1/6] Git.pm: Add config() method","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-07-03T20:47:55Z","receivedAt":"2006-07-03T20:47:55Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"This accessor will retrieve value(s) of the given configuration variable.\n\nSigned-off-by: Petr Baudis <pasky@suse.cz>\n---\n\n Documentation/git-repo-config.txt |    3 ++-\n perl/Git.pm                       |   37 ++++++++++++++++++++++++++++++++++++-\n repo-config.c                     |    2 +-\n 3 files changed, 39 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt\nindex 803c0d5..cc72fa9 100644\n--- a/Documentation/git-repo-config.txt\n+++ b/Documentation/git-repo-config.txt\n@@ -54,7 +54,8 @@ OPTIONS\n \n --get::\n \tGet the value for a given key (optionally filtered by a regex\n-\tmatching the value).\n+\tmatching the value). Returns error code 1 if the key was not\n+\tfound and error code 2 if multiple key values were found.\n \n --get-all::\n \tLike get, but does not fail if the number of values for the key\ndiff --git a/perl/Git.pm b/perl/Git.pm\nindex b4ee88b..24fd7ce 100644\n--- a/perl/Git.pm\n+++ b/perl/Git.pm\n@@ -473,7 +473,6 @@ and the directory must exist.\n \n sub wc_chdir {\n \tmy ($self, $subdir) = @_;\n-\n \t$self->wc_path()\n \t\tor throw Error::Simple(\"bare repository\");\n \n@@ -486,6 +485,42 @@ sub wc_chdir {\n }\n \n \n+=item config ( VARIABLE )\n+\n+Retrieve the configuration C<VARIABLE> in the same manner as C<repo-config>\n+does. In scalar context requires the variable to be set only one time\n+(exception is thrown otherwise), in array context returns allows the\n+variable to be set multiple times and returns all the values.\n+\n+Must be called on a repository instance.\n+\n+This currently wraps command('repo-config') so it is not so fast.\n+\n+=cut\n+\n+sub config {\n+\tmy ($self, $var) = @_;\n+\t$self->repo_path()\n+\t\tor throw Error::Simple(\"not a repository\");\n+\n+\ttry {\n+\t\tif (wantarray) {\n+\t\t\treturn $self->command('repo-config', '--get-all', $var);\n+\t\t} else {\n+\t\t\treturn $self->command_oneline('repo-config', '--get', $var);\n+\t\t}\n+\t} catch Git::Error::Command with {\n+\t\tmy $E = shift;\n+\t\tif ($E->value() == 1) {\n+\t\t\t# Key not found.\n+\t\t\treturn undef;\n+\t\t} else {\n+\t\t\tthrow $E;\n+\t\t}\n+\t};\n+}\n+\n+\n =item hash_object ( TYPE, FILENAME )\n \n =item hash_object ( TYPE, FILEHANDLE )\ndiff --git a/repo-config.c b/repo-config.c\nindex 743f02b..c7ed0ac 100644\n--- a/repo-config.c\n+++ b/repo-config.c\n@@ -118,7 +118,7 @@ static int get_value(const char* key_, c\n \tif (do_all)\n \t\tret = !seen;\n \telse\n-\t\tret =  (seen == 1) ? 0 : 1;\n+\t\tret = (seen == 1) ? 0 : seen > 1 ? 2 : 1;\n \n free_strings:\n \tif (repo_config)\n"},{"id":"23132","messageId":"20060703204758.28541.87116.stgit@machine.or.cz","threadId":"4751","inReplyTo":"20060703204415.28541.47920.stgit@machine.or.cz","subject":"[PATCH 2/6] Convert git-send-email to use Git.pm","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-07-03T20:47:58Z","receivedAt":"2006-07-03T20:47:58Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Signed-off-by: Petr Baudis <pasky@suse.cz>\n---\n\n git-send-email.perl |   30 ++++++++----------------------\n 1 files changed, 8 insertions(+), 22 deletions(-)\n\ndiff --git a/git-send-email.perl b/git-send-email.perl\nindex c5d9e73..e794e44 100755\n--- a/git-send-email.perl\n+++ b/git-send-email.perl\n@@ -21,6 +21,7 @@ use warnings;\n use Term::ReadLine;\n use Getopt::Long;\n use Data::Dumper;\n+use Git;\n \n # most mail servers generate the Date: header, but not all...\n $ENV{LC_ALL} = 'C';\n@@ -46,6 +47,8 @@ my $smtp_server;\n # Example reply to:\n #$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>';\n \n+my $repo = Git->repository();\n+\n my $term = new Term::ReadLine 'git-send-email';\n \n # Begin by accumulating all the variables (defined above), that we will end up\n@@ -81,23 +84,9 @@ foreach my $entry (@bcclist) {\n \n # Now, let's fill any that aren't set in with defaults:\n \n-sub gitvar {\n-    my ($var) = @_;\n-    my $fh;\n-    my $pid = open($fh, '-|');\n-    die \"$!\" unless defined $pid;\n-    if (!$pid) {\n-\texec('git-var', $var) or die \"$!\";\n-    }\n-    my ($val) = <$fh>;\n-    close $fh or die \"$!\";\n-    chomp($val);\n-    return $val;\n-}\n-\n sub gitvar_ident {\n     my ($name) = @_;\n-    my $val = gitvar($name);\n+    my $val = $repo->command('var', $name);\n     my @field = split(/\\s+/, $val);\n     return join(' ', @field[0...(@field-3)]);\n }\n@@ -106,8 +95,8 @@ my ($author) = gitvar_ident('GIT_AUTHOR_\n my ($committer) = gitvar_ident('GIT_COMMITTER_IDENT');\n \n my %aliases;\n-chomp(my @alias_files = `git-repo-config --get-all sendemail.aliasesfile`);\n-chomp(my $aliasfiletype = `git-repo-config sendemail.aliasfiletype`);\n+my @alias_files = $repo->config('sendemail.aliasesfile');\n+my $aliasfiletype = $repo->config('sendemail.aliasfiletype');\n my %parse_alias = (\n \t# multiline formats can be supported in the future\n \tmutt => sub { my $fh = shift; while (<$fh>) {\n@@ -132,7 +121,7 @@ my %parse_alias = (\n \t\t}}}\n );\n \n-if (@alias_files && defined $parse_alias{$aliasfiletype}) {\n+if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {\n \tforeach my $file (@alias_files) {\n \t\topen my $fh, '<', $file or die \"opening $file: $!\\n\";\n \t\t$parse_alias{$aliasfiletype}->($fh);\n@@ -374,10 +363,7 @@ sub send_message\n \tmy $date = strftime('%a, %d %b %Y %H:%M:%S %z', localtime($time++));\n \tmy $gitversion = '@@GIT_VERSION@@';\n \tif ($gitversion =~ m/..GIT_VERSION../) {\n-\t    $gitversion = `git --version`;\n-\t    chomp $gitversion;\n-\t    # keep only what's after the last space\n-\t    $gitversion =~ s/^.* //;\n+\t    $gitversion = Git::version();\n \t}\n \n \tmy $header = \"From: $from\n"},{"id":"23137","messageId":"20060703204800.28541.30447.stgit@machine.or.cz","threadId":"4751","inReplyTo":"20060703204415.28541.47920.stgit@machine.or.cz","subject":"[PATCH 3/6] Git.pm: Introduce ident() and ident_person() methods","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-07-03T20:48:01Z","receivedAt":"2006-07-03T20:48:01Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"These methods can retrieve/parse the author/committer ident.\n\nSigned-off-by: Petr Baudis <pasky@suse.cz>\n---\n\n git-send-email.perl |   11 ++---------\n perl/Git.pm         |   49 +++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 51 insertions(+), 9 deletions(-)\n\ndiff --git a/git-send-email.perl b/git-send-email.perl\nindex e794e44..79e82f5 100755\n--- a/git-send-email.perl\n+++ b/git-send-email.perl\n@@ -84,15 +84,8 @@ foreach my $entry (@bcclist) {\n \n # Now, let's fill any that aren't set in with defaults:\n \n-sub gitvar_ident {\n-    my ($name) = @_;\n-    my $val = $repo->command('var', $name);\n-    my @field = split(/\\s+/, $val);\n-    return join(' ', @field[0...(@field-3)]);\n-}\n-\n-my ($author) = gitvar_ident('GIT_AUTHOR_IDENT');\n-my ($committer) = gitvar_ident('GIT_COMMITTER_IDENT');\n+my ($author) = $repo->ident_person('author');\n+my ($committer) = $repo->ident_person('committer');\n \n my %aliases;\n my @alias_files = $repo->config('sendemail.aliasesfile');\ndiff --git a/perl/Git.pm b/perl/Git.pm\nindex 24fd7ce..9ce9fcd 100644\n--- a/perl/Git.pm\n+++ b/perl/Git.pm\n@@ -521,6 +521,55 @@ sub config {\n }\n \n \n+=item ident ( TYPE | IDENTSTR )\n+\n+=item ident_person ( TYPE | IDENTSTR | IDENTARRAY )\n+\n+This suite of functions retrieves and parses ident information, as stored\n+in the commit and tag objects or produced by C<var GIT_type_IDENT> (thus\n+C<TYPE> can be either I<author> or I<committer>; case is insignificant).\n+\n+The C<ident> method retrieves the ident information from C<git-var>\n+and either returns it as a scalar string or as an array with the fields parsed.\n+Alternatively, it can take a prepared ident string (e.g. from the commit\n+object) and just parse it.\n+\n+C<ident_person> returns the person part of the ident - name and email;\n+it can take the same arguments as C<ident> or the array returned by C<ident>.\n+\n+The synopsis is like:\n+\n+\tmy ($name, $email, $time_tz) = ident('author');\n+\t\"$name <$email>\" eq ident_person('author');\n+\t\"$name <$email>\" eq ident_person($name);\n+\t$time_tz =~ /^\\d+ [+-]\\d{4}$/;\n+\n+Both methods must be called on a repository instance.\n+\n+=cut\n+\n+sub ident {\n+\tmy ($self, $type) = @_;\n+\tmy $identstr;\n+\tif (lc $type eq lc 'committer' or lc $type eq lc 'author') {\n+\t\t$identstr = $self->command_oneline('var', 'GIT_'.uc($type).'_IDENT');\n+\t} else {\n+\t\t$identstr = $type;\n+\t}\n+\tif (wantarray) {\n+\t\treturn $identstr =~ /^(.*) <(.*)> (\\d+ [+-]\\d{4})$/;\n+\t} else {\n+\t\treturn $identstr;\n+\t}\n+}\n+\n+sub ident_person {\n+\tmy ($self, @ident) = @_;\n+\t$#ident == 0 and @ident = $self->ident($ident[0]);\n+\treturn \"$ident[0] <$ident[1]>\";\n+}\n+\n+\n =item hash_object ( TYPE, FILENAME )\n \n =item hash_object ( TYPE, FILEHANDLE )\n"},{"id":"23131","messageId":"20060703204803.28541.67315.stgit@machine.or.cz","threadId":"4751","inReplyTo":"20060703204415.28541.47920.stgit@machine.or.cz","subject":"[PATCH 4/6] Make it possible to set up libgit directly (instead of from the environment)","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-07-03T20:48:03Z","receivedAt":"2006-07-03T20:48:03Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"This introduces a setup_git() function which is essentialy a (public)\nbackend for setup_git_env() which lets anyone specify custom sources\nfor the various paths instead of environment variables. Since the repositories\nmay get switched on the fly, this also updates code that caches paths to\ninvalidate them properly; I hope neither of those is a sweet spot.\n\nIt is used by Git.xs' xs__call_gate() to set up per-repository data\nfor libgit's consumption. No code actually takes advantage of it yet\nbut get_object() will in the next patches.\n\nSigned-off-by: Petr Baudis <pasky@suse.cz>\n---\n\n cache.h       |    3 +++\n commit.c      |   23 +++++++++++++++++++----\n environment.c |   45 +++++++++++++++++++++++++++++++++++++++------\n perl/Git.pm   |    8 ++++----\n perl/Git.xs   |   16 +++++++++++++++-\n sha1_file.c   |   30 ++++++++++++++++++++++++------\n sha1_name.c   |   10 ++++++++--\n 7 files changed, 112 insertions(+), 23 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 8719939..962f2fc 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -116,6 +116,9 @@ extern struct cache_entry **active_cache\n extern unsigned int active_nr, active_alloc, active_cache_changed;\n extern struct cache_tree *active_cache_tree;\n \n+extern void setup_git(char *new_git_dir, char *new_git_object_dir,\n+                      char *new_git_index_file, char *new_git_graft_file);\n+\n #define GIT_DIR_ENVIRONMENT \"GIT_DIR\"\n #define DEFAULT_GIT_DIR_ENVIRONMENT \".git\"\n #define DB_ENVIRONMENT \"GIT_OBJECT_DIRECTORY\"\ndiff --git a/commit.c b/commit.c\nindex a608faf..91f97c1 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -163,6 +163,14 @@ int register_commit_graft(struct commit_\n \treturn 0;\n }\n \n+void free_commit_grafts(void)\n+{\n+\tint pos = commit_graft_nr;\n+\twhile (pos >= 0)\n+\t\tfree(commit_graft[pos--]);\n+\tcommit_graft_nr = 0;\n+}\n+\n struct commit_graft *read_graft_line(char *buf, int len)\n {\n \t/* The format is just \"Commit Parent1 Parent2 ...\\n\" */\n@@ -215,11 +223,18 @@ int read_graft_file(const char *graft_fi\n static void prepare_commit_graft(void)\n {\n \tstatic int commit_graft_prepared;\n-\tchar *graft_file;\n+\tstatic char *last_graft_file;\n+\tchar *graft_file = get_graft_file();\n+\n+\tif (last_graft_file) {\n+\t\tif (!strcmp(graft_file, last_graft_file))\n+\t\t\treturn;\n+\t\tfree_commit_grafts();\n+\t}\n+\tif (last_graft_file)\n+\t\tfree(last_graft_file);\n+\tlast_graft_file = strdup(graft_file);\n \n-\tif (commit_graft_prepared)\n-\t\treturn;\n-\tgraft_file = get_graft_file();\n \tread_graft_file(graft_file);\n \tcommit_graft_prepared = 1;\n }\ndiff --git a/environment.c b/environment.c\nindex 3de8eb3..6b64d11 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -21,28 +21,61 @@ char git_commit_encoding[MAX_ENCODING_LE\n int shared_repository = PERM_UMASK;\n const char *apply_default_whitespace = NULL;\n \n+static int dyn_git_object_dir, dyn_git_index_file, dyn_git_graft_file;\n static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir,\n \t*git_graft_file;\n-static void setup_git_env(void)\n+\n+void setup_git(char *new_git_dir, char *new_git_object_dir,\n+               char *new_git_index_file, char *new_git_graft_file)\n {\n-\tgit_dir = getenv(GIT_DIR_ENVIRONMENT);\n+\tgit_dir = new_git_dir;\n \tif (!git_dir)\n \t\tgit_dir = DEFAULT_GIT_DIR_ENVIRONMENT;\n-\tgit_object_dir = getenv(DB_ENVIRONMENT);\n+\n+\tif (dyn_git_object_dir)\n+\t\tfree(git_object_dir);\n+\tgit_object_dir = new_git_object_dir;\n \tif (!git_object_dir) {\n \t\tgit_object_dir = xmalloc(strlen(git_dir) + 9);\n \t\tsprintf(git_object_dir, \"%s/objects\", git_dir);\n+\t\tdyn_git_object_dir = 1;\n+\t} else {\n+\t\tdyn_git_object_dir = 0;\n \t}\n+\n+\tif (git_refs_dir)\n+\t\tfree(git_refs_dir);\n \tgit_refs_dir = xmalloc(strlen(git_dir) + 6);\n \tsprintf(git_refs_dir, \"%s/refs\", git_dir);\n-\tgit_index_file = getenv(INDEX_ENVIRONMENT);\n+\n+\tif (dyn_git_index_file)\n+\t\tfree(git_index_file);\n+\tgit_index_file = new_git_index_file;\n \tif (!git_index_file) {\n \t\tgit_index_file = xmalloc(strlen(git_dir) + 7);\n \t\tsprintf(git_index_file, \"%s/index\", git_dir);\n+\t\tdyn_git_index_file = 1;\n+\t} else {\n+\t\tdyn_git_index_file = 0;\n \t}\n-\tgit_graft_file = getenv(GRAFT_ENVIRONMENT);\n-\tif (!git_graft_file)\n+\n+\tif (dyn_git_graft_file)\n+\t\tfree(git_graft_file);\n+\tgit_graft_file = new_git_graft_file;\n+\tif (!git_graft_file) {\n \t\tgit_graft_file = strdup(git_path(\"info/grafts\"));\n+\t\tdyn_git_graft_file = 1;\n+\t} else {\n+\t\tdyn_git_graft_file = 0;\n+\t}\n+}\n+\n+static void setup_git_env(void)\n+{\n+\tsetup_git(getenv(GIT_DIR_ENVIRONMENT),\n+\t          getenv(DB_ENVIRONMENT),\n+\t          getenv(INDEX_ENVIRONMENT),\n+\t          getenv(GRAFT_ENVIRONMENT));\n }\n \n char *get_git_dir(void)\ndiff --git a/perl/Git.pm b/perl/Git.pm\nindex 9ce9fcd..895a939 100644\n--- a/perl/Git.pm\n+++ b/perl/Git.pm\n@@ -92,6 +92,7 @@ increate nonwithstanding).\n use Carp qw(carp croak); # but croak is bad - throw instead\n use Error qw(:try);\n use Cwd qw(abs_path);\n+use Scalar::Util;\n \n require XSLoader;\n XSLoader::load('Git', $VERSION);\n@@ -833,11 +834,10 @@ sub _call_gate {\n \tif (defined $self) {\n \t\t# XXX: We ignore the WorkingCopy! To properly support\n \t\t# that will require heavy changes in libgit.\n+\t\t# For now, when we will need to do it we could temporarily\n+\t\t# chdir() there and then chdir() back after the call is done.\n \n-\t\t# XXX: And we ignore everything else as well. libgit\n-\t\t# at least needs to be extended to let us specify\n-\t\t# the $GIT_DIR instead of looking it up in environment.\n-\t\t#xs_call_gate($self->{opts}->{Repository});\n+\t\txs__call_gate(Scalar::Util::refaddr($self), $self->repo_path());\n \t}\n \n \t# Having to call throw from the C code is a sure path to insanity.\ndiff --git a/perl/Git.xs b/perl/Git.xs\nindex 2bbec43..6ed26a2 100644\n--- a/perl/Git.xs\n+++ b/perl/Git.xs\n@@ -52,7 +52,21 @@ BOOT:\n }\n \n \n-# /* TODO: xs_call_gate(). See Git.pm. */\n+void\n+xs__call_gate(repoid, git_dir)\n+\tlong repoid;\n+\tchar *git_dir;\n+CODE:\n+{\n+\tstatic long last_repoid;\n+\tif (repoid != last_repoid) {\n+\t\tsetup_git(git_dir,\n+\t\t          getenv(DB_ENVIRONMENT),\n+\t\t          getenv(INDEX_ENVIRONMENT),\n+\t\t          getenv(GRAFT_ENVIRONMENT));\n+\t\tlast_repoid = repoid;\n+\t}\n+}\n \n \n char *\ndiff --git a/sha1_file.c b/sha1_file.c\nindex 8179630..ab64543 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -126,16 +126,22 @@ static void fill_sha1_path(char *pathbuf\n char *sha1_file_name(const unsigned char *sha1)\n {\n \tstatic char *name, *base;\n+\tstatic const char *last_objdir;\n+\tconst char *sha1_file_directory = get_object_directory();\n \n-\tif (!base) {\n-\t\tconst char *sha1_file_directory = get_object_directory();\n+\tif (!last_objdir || strcmp(last_objdir, sha1_file_directory)) {\n \t\tint len = strlen(sha1_file_directory);\n+\t\tif (base)\n+\t\t\tfree(base);\n \t\tbase = xmalloc(len + 60);\n \t\tmemcpy(base, sha1_file_directory, len);\n \t\tmemset(base+len, 0, 60);\n \t\tbase[len] = '/';\n \t\tbase[len+3] = '/';\n \t\tname = base + len + 1;\n+\t\tif (last_objdir)\n+\t\t\tfree((char *) last_objdir);\n+\t\tlast_objdir = strdup(sha1_file_directory);\n \t}\n \tfill_sha1_path(name, sha1);\n \treturn base;\n@@ -145,14 +151,20 @@ char *sha1_pack_name(const unsigned char\n {\n \tstatic const char hex[] = \"0123456789abcdef\";\n \tstatic char *name, *base, *buf;\n+\tstatic const char *last_objdir;\n+\tconst char *sha1_file_directory = get_object_directory();\n \tint i;\n \n-\tif (!base) {\n-\t\tconst char *sha1_file_directory = get_object_directory();\n+\tif (!last_objdir || strcmp(last_objdir, sha1_file_directory)) {\n \t\tint len = strlen(sha1_file_directory);\n+\t\tif (base)\n+\t\t\tfree(base);\n \t\tbase = xmalloc(len + 60);\n \t\tsprintf(base, \"%s/pack/pack-1234567890123456789012345678901234567890.pack\", sha1_file_directory);\n \t\tname = base + len + 11;\n+\t\tif (last_objdir)\n+\t\t\tfree((char *) last_objdir);\n+\t\tlast_objdir = strdup(sha1_file_directory);\n \t}\n \n \tbuf = name;\n@@ -170,14 +182,20 @@ char *sha1_pack_index_name(const unsigne\n {\n \tstatic const char hex[] = \"0123456789abcdef\";\n \tstatic char *name, *base, *buf;\n+\tstatic const char *last_objdir;\n+\tconst char *sha1_file_directory = get_object_directory();\n \tint i;\n \n-\tif (!base) {\n-\t\tconst char *sha1_file_directory = get_object_directory();\n+\tif (!last_objdir || strcmp(last_objdir, sha1_file_directory)) {\n \t\tint len = strlen(sha1_file_directory);\n+\t\tif (base)\n+\t\t\tfree(base);\n \t\tbase = xmalloc(len + 60);\n \t\tsprintf(base, \"%s/pack/pack-1234567890123456789012345678901234567890.idx\", sha1_file_directory);\n \t\tname = base + len + 11;\n+\t\tif (last_objdir)\n+\t\t\tfree((char *) last_objdir);\n+\t\tlast_objdir = strdup(sha1_file_directory);\n \t}\n \n \tbuf = name;\ndiff --git a/sha1_name.c b/sha1_name.c\nindex f2cbafa..c698c1b 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -12,15 +12,21 @@ static int find_short_object_filename(in\n \tchar hex[40];\n \tint found = 0;\n \tstatic struct alternate_object_database *fakeent;\n+\tstatic const char *last_objdir;\n+\tconst char *objdir = get_object_directory();\n \n-\tif (!fakeent) {\n-\t\tconst char *objdir = get_object_directory();\n+\tif (!last_objdir || strcmp(last_objdir, objdir)) {\n \t\tint objdir_len = strlen(objdir);\n \t\tint entlen = objdir_len + 43;\n+\t\tif (fakeent)\n+\t\t\tfree(fakeent);\n \t\tfakeent = xmalloc(sizeof(*fakeent) + entlen);\n \t\tmemcpy(fakeent->base, objdir, objdir_len);\n \t\tfakeent->name = fakeent->base + objdir_len + 1;\n \t\tfakeent->name[-1] = '/';\n+\t\tif (last_objdir)\n+\t\t\tfree((char *) last_objdir);\n+\t\tlast_objdir = strdup(objdir);\n \t}\n \tfakeent->next = alt_odb_list;\n \n"},{"id":"23135","messageId":"20060703204805.28541.62824.stgit@machine.or.cz","threadId":"4751","inReplyTo":"20060703204415.28541.47920.stgit@machine.or.cz","subject":"[PATCH 5/6] Git.pm: Introduce fast get_object() method","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-07-03T20:48:05Z","receivedAt":"2006-07-03T20:48:05Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Direct .xs routine. Note that it does not work 100% correctly when\nyou juggle multiple repository objects, but it is not that bad either.\nThe trouble is that we might reuse packs information for another\nGit project; that is not an issue since Git depends on uniqueness\nof SHA1 ids so if we have found the object somewhere else, it is\nnevertheless going to be the same object. It merely makes object\nexistence detection through this method unreliable; it is duly noted\nin the documentation.\n\nAt least that's how I see it, I hope I didn't overlook any other\npotential problem. I tested it for memory leaks and it appears to be\ndoing ok.\n\nSigned-off-by: Petr Baudis <pasky@suse.cz>\n---\n\n perl/Git.pm |   18 ++++++++++++++++++\n perl/Git.xs |   24 ++++++++++++++++++++++++\n 2 files changed, 42 insertions(+), 0 deletions(-)\n\ndiff --git a/perl/Git.pm b/perl/Git.pm\nindex 895a939..65acaa7 100644\n--- a/perl/Git.pm\n+++ b/perl/Git.pm\n@@ -571,6 +571,24 @@ sub ident_person {\n }\n \n \n+=item get_object ( TYPE, SHA1 )\n+\n+Return contents of the given object in a scalar string. If the object has\n+not been found, undef is returned; however, do not rely on this! Currently,\n+if you use multiple repositories at once, get_object() on one repository\n+_might_ return the object even though it exists only in another repository.\n+(But do not rely on this behaviour either.)\n+\n+The method must be called on a repository instance.\n+\n+Implementation of this method is very fast; no external command calls\n+are involved. That's why it is broken, too. ;-)\n+\n+=cut\n+\n+# Implemented in Git.xs.\n+\n+\n =item hash_object ( TYPE, FILENAME )\n \n =item hash_object ( TYPE, FILEHANDLE )\ndiff --git a/perl/Git.xs b/perl/Git.xs\nindex 6ed26a2..226dd4f 100644\n--- a/perl/Git.xs\n+++ b/perl/Git.xs\n@@ -111,6 +111,30 @@ CODE:\n \tfree((char **) argv);\n }\n \n+\n+SV *\n+xs_get_object(type, id)\n+\tchar *type;\n+\tchar *id;\n+CODE:\n+{\n+\tunsigned char sha1[20];\n+\tunsigned long size;\n+\tvoid *buf;\n+\n+\tif (strlen(id) != 40 || get_sha1_hex(id, sha1) < 0)\n+\t\tXSRETURN_UNDEF;\n+\n+\tbuf = read_sha1_file(sha1, type, &size);\n+\tif (!buf)\n+\t\tXSRETURN_UNDEF;\n+\tRETVAL = newSVpvn(buf, size);\n+\tfree(buf);\n+}\n+OUTPUT:\n+\tRETVAL\n+\n+\n char *\n xs_hash_object_pipe(type, fd)\n \tchar *type;\n"},{"id":"23136","messageId":"20060703204807.28541.5367.stgit@machine.or.cz","threadId":"4751","inReplyTo":"20060703204415.28541.47920.stgit@machine.or.cz","subject":"[PATCH 6/6] Convert git-annotate to use Git.pm","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-07-03T20:48:07Z","receivedAt":"2006-07-03T20:48:07Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Together with the other converted scripts, this is probably still pu\nmaterial; it appears to work fine for me, though. The speed gain from\nget_object() is about 10% (I expected more...).\n\nSigned-off-by: Petr Baudis <pasky@suse.cz>\n---\n\n git-annotate.perl |  167 ++++++++++-------------------------------------------\n 1 files changed, 31 insertions(+), 136 deletions(-)\n\ndiff --git a/git-annotate.perl b/git-annotate.perl\nindex a6a7a48..d924e87 100755\n--- a/git-annotate.perl\n+++ b/git-annotate.perl\n@@ -11,6 +11,7 @@ use strict;\n use Getopt::Long;\n use POSIX qw(strftime gmtime);\n use File::Basename qw(basename dirname);\n+use Git;\n \n sub usage() {\n \tprint STDERR \"Usage: ${\\basename $0} [-s] [-S revs-file] file [ revision ]\n@@ -29,7 +30,7 @@ sub usage() {\n \texit(1);\n }\n \n-our ($help, $longrev, $rename, $rawtime, $starting_rev, $rev_file) = (0, 0, 1);\n+our ($help, $longrev, $rename, $rawtime, $starting_rev, $rev_file, $repo) = (0, 0, 1);\n \n my $rc = GetOptions(\t\"long|l\" => \\$longrev,\n \t\t\t\"time|t\" => \\$rawtime,\n@@ -52,6 +53,8 @@ my @stack = (\n \t},\n );\n \n+$repo = Git->repository();\n+\n our @filelines = ();\n \n if (defined $starting_rev) {\n@@ -102,15 +105,11 @@ while (my $bound = pop @stack) {\n push @revqueue, $head;\n init_claim( defined $starting_rev ? $head : 'dirty');\n unless (defined $starting_rev) {\n-\tmy $diff = open_pipe(\"git\",\"diff\",\"-R\", \"HEAD\", \"--\",$filename)\n-\t\tor die \"Failed to call git diff to check for dirty state: $!\";\n-\n-\t_git_diff_parse($diff, $head, \"dirty\", (\n-\t\t\t\t'author' => gitvar_name(\"GIT_AUTHOR_IDENT\"),\n-\t\t\t\t'author_date' => sprintf(\"%s +0000\",time()),\n-\t\t\t\t)\n-\t\t\t);\n-\tclose($diff);\n+\tmy %ident;\n+\t@ident{'author', 'author_email', 'author_date'} = $repo->ident('author');\n+\tmy $diff = $repo->command_output_pipe('diff', '-R', 'HEAD', '--', $filename);\n+\t_git_diff_parse($diff, $head, \"dirty\", %ident);\n+\t$repo->command_close_pipe($diff);\n }\n handle_rev();\n \n@@ -181,8 +180,7 @@ sub git_rev_list {\n \t\topen($revlist, '<' . $rev_file)\n \t\t    or die \"Failed to open $rev_file : $!\";\n \t} else {\n-\t\t$revlist = open_pipe(\"git-rev-list\",\"--parents\",\"--remove-empty\",$rev,\"--\",$file)\n-\t\t\tor die \"Failed to exec git-rev-list: $!\";\n+\t\t$revlist = $repo->command_output_pipe('rev-list', '--parents', '--remove-empty', $rev, '--', $file);\n \t}\n \n \tmy @revs;\n@@ -191,7 +189,7 @@ sub git_rev_list {\n \t\tmy ($rev, @parents) = split /\\s+/, $line;\n \t\tpush @revs, [ $rev, @parents ];\n \t}\n-\tclose($revlist);\n+\t$repo->command_close_pipe($revlist);\n \n \tprintf(\"0 revs found for rev %s (%s)\\n\", $rev, $file) if (@revs == 0);\n \treturn @revs;\n@@ -200,8 +198,7 @@ sub git_rev_list {\n sub find_parent_renames {\n \tmy ($rev, $file) = @_;\n \n-\tmy $patch = open_pipe(\"git-diff-tree\", \"-M50\", \"-r\",\"--name-status\", \"-z\",\"$rev\")\n-\t\tor die \"Failed to exec git-diff: $!\";\n+\tmy $patch = $repo->command_output_pipe('diff-tree', '-M50', '-r', '--name-status', '-z', $rev);\n \n \tlocal $/ = \"\\0\";\n \tmy %bound;\n@@ -227,7 +224,7 @@ sub find_parent_renames {\n \t\t\t}\n \t\t}\n \t}\n-\tclose($patch);\n+\t$repo->command_close_pipe($patch);\n \n \treturn \\%bound;\n }\n@@ -236,14 +233,9 @@ sub find_parent_renames {\n sub git_find_parent {\n \tmy ($rev, $filename) = @_;\n \n-\tmy $revparent = open_pipe(\"git-rev-list\",\"--remove-empty\", \"--parents\",\"--max-count=1\",\"$rev\",\"--\",$filename)\n-\t\tor die \"Failed to open git-rev-list to find a single parent: $!\";\n-\n-\tmy $parentline = <$revparent>;\n-\tchomp $parentline;\n-\tmy ($revfound,$parent) = split m/\\s+/, $parentline;\n-\n-\tclose($revparent);\n+\tmy $parentline = $repo->command_oneline('rev-list', '--remove-empty',\n+\t\t\t'--parents', '--max-count=1', $rev, '--', $filename);\n+\tmy ($revfound, $parent) = split m/\\s+/, $parentline;\n \n \treturn $parent;\n }\n@@ -254,13 +246,13 @@ # Record the commit information that res\n sub git_diff_parse {\n \tmy ($parent, $rev, %revinfo) = @_;\n \n-\tmy $diff = open_pipe(\"git-diff-tree\",\"-M\",\"-p\",$rev,$parent,\"--\",\n-\t\t\t$revs{$rev}{'filename'}, $revs{$parent}{'filename'})\n-\t\tor die \"Failed to call git-diff for annotation: $!\";\n+\tmy $diff = $repo->command_output_pipe('diff-tree', '-M', '-p',\n+\t\t\t$rev, $parent, '--',\n+\t\t\t$revs{$rev}{'filename'}, $revs{$parent}{'filename'});\n \n \t_git_diff_parse($diff, $parent, $rev, %revinfo);\n \n-\tclose($diff);\n+\t$repo->command_close_pipe($diff);\n }\n \n sub _git_diff_parse {\n@@ -351,36 +343,25 @@ sub git_cat_file {\n \tmy $blob = git_ls_tree($rev, $filename);\n \tdie \"Failed to find a blob for $filename in rev $rev\\n\" if !defined $blob;\n \n-\tmy $catfile = open_pipe(\"git\",\"cat-file\", \"blob\", $blob)\n-\t\tor die \"Failed to git-cat-file blob $blob (rev $rev, file $filename): \" . $!;\n-\n-\tmy @lines;\n-\twhile(<$catfile>) {\n-\t\tchomp;\n-\t\tpush @lines, $_;\n-\t}\n-\tclose($catfile);\n-\n+\tmy @lines = split(/\\n/, $repo->get_object('blob', $blob));\n+\tpop @lines unless $lines[$#lines]; # Trailing newline\n \treturn @lines;\n }\n \n sub git_ls_tree {\n \tmy ($rev, $filename) = @_;\n \n-\tmy $lstree = open_pipe(\"git\",\"ls-tree\",$rev,$filename)\n-\t\tor die \"Failed to call git ls-tree: $!\";\n-\n+\tmy $lstree = $repo->command_output_pipe('ls-tree', $rev, $filename);\n \tmy ($mode, $type, $blob, $tfilename);\n \twhile(<$lstree>) {\n \t\tchomp;\n \t\t($mode, $type, $blob, $tfilename) = split(/\\s+/, $_, 4);\n \t\tlast if ($tfilename eq $filename);\n \t}\n-\tclose($lstree);\n+\t$repo->command_close_pipe($lstree);\n \n \treturn $blob if ($tfilename eq $filename);\n \tdie \"git-ls-tree failed to find blob for $filename\";\n-\n }\n \n \n@@ -396,25 +377,17 @@ sub claim_line {\n \n sub git_commit_info {\n \tmy ($rev) = @_;\n-\tmy $commit = open_pipe(\"git-cat-file\", \"commit\", $rev)\n-\t\tor die \"Failed to call git-cat-file: $!\";\n+\tmy $commit = $repo->get_object('commit', $rev);\n \n \tmy %info;\n-\twhile(<$commit>) {\n-\t\tchomp;\n-\t\tlast if (length $_ == 0);\n-\n-\t\tif (m/^author (.*) <(.*)> (.*)$/) {\n-\t\t\t$info{'author'} = $1;\n-\t\t\t$info{'author_email'} = $2;\n-\t\t\t$info{'author_date'} = $3;\n-\t\t} elsif (m/^committer (.*) <(.*)> (.*)$/) {\n-\t\t\t$info{'committer'} = $1;\n-\t\t\t$info{'committer_email'} = $2;\n-\t\t\t$info{'committer_date'} = $3;\n+\twhile ($commit =~ /(.*?)\\n/g) {\n+\t\tmy $line = $1;\n+\t\tif ($line =~ s/^author //) {\n+\t\t\t@info{'author', 'author_email', 'author_date'} = $repo->ident($line);\n+\t\t} elsif ($line =~ s/^committer//) {\n+\t\t\t@info{'committer', 'committer_email', 'committer_date'} = $repo->ident($line);\n \t\t}\n \t}\n-\tclose($commit);\n \n \treturn %info;\n }\n@@ -432,81 +405,3 @@ sub format_date {\n \tmy $t = $timestamp + $minutes * 60;\n \treturn strftime(\"%Y-%m-%d %H:%M:%S \" . $timezone, gmtime($t));\n }\n-\n-# Copied from git-send-email.perl - We need a Git.pm module..\n-sub gitvar {\n-    my ($var) = @_;\n-    my $fh;\n-    my $pid = open($fh, '-|');\n-    die \"$!\" unless defined $pid;\n-    if (!$pid) {\n-\texec('git-var', $var) or die \"$!\";\n-    }\n-    my ($val) = <$fh>;\n-    close $fh or die \"$!\";\n-    chomp($val);\n-    return $val;\n-}\n-\n-sub gitvar_name {\n-    my ($name) = @_;\n-    my $val = gitvar($name);\n-    my @field = split(/\\s+/, $val);\n-    return join(' ', @field[0...(@field-4)]);\n-}\n-\n-sub open_pipe {\n-\tif ($^O eq '##INSERT_ACTIVESTATE_STRING_HERE##') {\n-\t\treturn open_pipe_activestate(@_);\n-\t} else {\n-\t\treturn open_pipe_normal(@_);\n-\t}\n-}\n-\n-sub open_pipe_activestate {\n-\ttie *fh, \"Git::ActiveStatePipe\", @_;\n-\treturn *fh;\n-}\n-\n-sub open_pipe_normal {\n-\tmy (@execlist) = @_;\n-\n-\tmy $pid = open my $kid, \"-|\";\n-\tdefined $pid or die \"Cannot fork: $!\";\n-\n-\tunless ($pid) {\n-\t\texec @execlist;\n-\t\tdie \"Cannot exec @execlist: $!\";\n-\t}\n-\n-\treturn $kid;\n-}\n-\n-package Git::ActiveStatePipe;\n-use strict;\n-\n-sub TIEHANDLE {\n-\tmy ($class, @params) = @_;\n-\tmy $cmdline = join \" \", @params;\n-\tmy  @data = qx{$cmdline};\n-\tbless { i => 0, data => \\@data }, $class;\n-}\n-\n-sub READLINE {\n-\tmy $self = shift;\n-\tif ($self->{i} >= scalar @{$self->{data}}) {\n-\t\treturn undef;\n-\t}\n-\treturn $self->{'data'}->[ $self->{i}++ ];\n-}\n-\n-sub CLOSE {\n-\tmy $self = shift;\n-\tdelete $self->{data};\n-\tdelete $self->{i};\n-}\n-\n-sub EOF {\n-\tmy $self = shift;\n-\treturn ($self->{i} >= scalar @{$self->{data}});\n-}\n"},{"id":"23144","messageId":"20060703213059.GO29115@pasky.or.cz","threadId":"4751","inReplyTo":"20060703204803.28541.67315.stgit@machine.or.cz","subject":"Re: [PATCH 4/6] Make it possible to set up libgit directly (instead of from the environment)","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-07-03T21:30:59Z","receivedAt":"2006-07-03T21:30:59Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Dear diary, on Mon, Jul 03, 2006 at 10:48:03PM CEST, I got a letter\nwhere Petr Baudis <pasky@suse.cz> said that...\n> This introduces a setup_git() function which is essentialy a (public)\n> backend for setup_git_env() which lets anyone specify custom sources\n> for the various paths instead of environment variables. Since the repositories\n> may get switched on the fly, this also updates code that caches paths to\n> invalidate them properly; I hope neither of those is a sweet spot.\n> \n> It is used by Git.xs' xs__call_gate() to set up per-repository data\n> for libgit's consumption. No code actually takes advantage of it yet\n> but get_object() will in the next patches.\n> \n> Signed-off-by: Petr Baudis <pasky@suse.cz>\n\nTo further clarify, this only invalidates the path cache and grafts\nlist, not alternates (it assumes the environment variable stays the\nsame for now; that is to be fixed when we extend Git.pm further)\nand not pack list - we will automagically extend the list of packs when\nwe meet more repositories, but we will never remove old packs from the\nlist. (For no special reason other than this does no harm other than\npossibly finding objects that should be missing, and the patch smells\nbad enough enough as it is now. ;-)\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\nSnow falling on Perl. White noise covering line noise.\nHides all the bugs too. -- J. Putnam\n"},{"id":"23146","messageId":"20060703214956.GP29115@pasky.or.cz","threadId":"4751","inReplyTo":"20060703204803.28541.67315.stgit@machine.or.cz","subject":"[PATCH] Eliminate Scalar::Util for something simpler","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-07-03T21:49:56Z","receivedAt":"2006-07-03T21:49:56Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Dear diary, on Mon, Jul 03, 2006 at 10:48:03PM CEST, I got a letter\nwhere Petr Baudis <pasky@suse.cz> said that...\n> +\t\txs__call_gate(Scalar::Util::refaddr($self), $self->repo_path());\n\nThis was silly and requires Scalar::Util.\n\n->8-\nSigned-off-by: Petr Baudis <pasky@suse.cz>\n---\n\n perl/Git.pm |    7 ++++---\n 1 files changed, 4 insertions(+), 3 deletions(-)\n\ndiff --git a/perl/Git.pm b/perl/Git.pm\nindex 65acaa7..f2467bd 100644\n--- a/perl/Git.pm\n+++ b/perl/Git.pm\n@@ -92,13 +92,14 @@ increate nonwithstanding).\n use Carp qw(carp croak); # but croak is bad - throw instead\n use Error qw(:try);\n use Cwd qw(abs_path);\n-use Scalar::Util;\n \n require XSLoader;\n XSLoader::load('Git', $VERSION);\n \n }\n \n+my $instance_id = 0;\n+\n \n =head1 CONSTRUCTORS\n \n@@ -216,7 +217,7 @@ sub repository {\n \t\tdelete $opts{Directory};\n \t}\n \n-\t$self = { opts => \\%opts };\n+\t$self = { opts => \\%opts, id => $instance_id++ };\n \tbless $self, $class;\n }\n \n@@ -855,7 +856,7 @@ sub _call_gate {\n \t\t# For now, when we will need to do it we could temporarily\n \t\t# chdir() there and then chdir() back after the call is done.\n \n-\t\txs__call_gate(Scalar::Util::refaddr($self), $self->repo_path());\n+\t\txs__call_gate($self->{id}, $self->repo_path());\n \t}\n \n \t# Having to call throw from the C code is a sure path to insanity.\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\nSnow falling on Perl. White noise covering line noise.\nHides all the bugs too. -- J. Putnam\n"}]}