{"thread":{"id":"4662","subject":"[PATCH 1/7] Git.pm: Introduce ident() and ident_person() methods","startedAt":"2006-06-25T01:54:21Z","lastAt":"2006-06-25T09:36:18Z","messageCount":11,"participants":["Petr Baudis","Junio C Hamano","Ryan Anderson"],"isPatch":true,"patchVersion":1,"patchTotal":7},"messages":[{"id":"22503","messageId":"20060625015421.29906.50002.stgit@machine.or.cz","threadId":"4662","inReplyTo":null,"subject":"[PATCH 1/7] Git.pm: Introduce ident() and ident_person() methods","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-06-25T01:54:21Z","receivedAt":"2006-06-25T01:54:21Z","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         |   50 ++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 52 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 2e1241b..08f56c0 100644\n--- a/perl/Git.pm\n+++ b/perl/Git.pm\n@@ -520,6 +520,56 @@ 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+\n =item hash_object ( FILENAME [, TYPE ] )\n \n =item hash_object ( FILEHANDLE [, TYPE ] )\n"},{"id":"22505","messageId":"20060625015423.29906.86666.stgit@machine.or.cz","threadId":"4662","inReplyTo":"20060625015421.29906.50002.stgit@machine.or.cz","subject":"[PATCH 2/7] Git.pm: Try to support ActiveState output pipe","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-06-25T01:54:23Z","receivedAt":"2006-06-25T01:54:23Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"The code is stolen from git-annotate and completely untested since\nI don't have access to any Microsoft operating system now. Someone\nActiveState-savvy should look at it anyway and try to implement\nthe input pipe as well, if it is possible at all; also, the implementation\nseems to be horribly whitespace-unsafe.\n\nSigned-off-by: Petr Baudis <pasky@suse.cz>\n---\n\n perl/Git.pm |   68 +++++++++++++++++++++++++++++++++++++++++++++++++----------\n 1 files changed, 57 insertions(+), 11 deletions(-)\n\ndiff --git a/perl/Git.pm b/perl/Git.pm\nindex 08f56c0..6da11a6 100644\n--- a/perl/Git.pm\n+++ b/perl/Git.pm\n@@ -748,18 +748,29 @@ sub _command_common_pipe {\n \t}\n \t_check_valid_cmd($cmd);\n \n-\tmy $pid = open(my $fh, $direction);\n-\tif (not defined $pid) {\n-\t\tthrow Error::Simple(\"open failed: $!\");\n-\t} elsif ($pid == 0) {\n-\t\tif (defined $opts{STDERR}) {\n-\t\t\tclose STDERR;\n-\t\t}\n-\t\tif ($opts{STDERR}) {\n-\t\t\topen (STDERR, '>&', $opts{STDERR})\n-\t\t\t\tor die \"dup failed: $!\";\n+\tmy $fh;\n+\tif ($^O eq '##INSERT_ACTIVESTATE_STRING_HERE##') {\n+\t\t# ActiveState Perl\n+\t\t#defined $opts{STDERR} and\n+\t\t#\twarn 'ignoring STDERR option - running w/ ActiveState';\n+\t\t$direction eq '-|' or\n+\t\t\tdie 'input pipe for ActiveState not implemented';\n+\t\ttie ($fh, 'Git::activestate_pipe', $cmd, @args);\n+\n+\t} else {\n+\t\tmy $pid = open($fh, $direction);\n+\t\tif (not defined $pid) {\n+\t\t\tthrow Error::Simple(\"open failed: $!\");\n+\t\t} elsif ($pid == 0) {\n+\t\t\tif (defined $opts{STDERR}) {\n+\t\t\t\tclose STDERR;\n+\t\t\t}\n+\t\t\tif ($opts{STDERR}) {\n+\t\t\t\topen (STDERR, '>&', $opts{STDERR})\n+\t\t\t\t\tor die \"dup failed: $!\";\n+\t\t\t}\n+\t\t\t_cmd_exec($self, $cmd, @args);\n \t\t}\n-\t\t_cmd_exec($self, $cmd, @args);\n \t}\n \treturn wantarray ? ($fh, join(' ', $cmd, @args)) : $fh;\n }\n@@ -834,4 +845,39 @@ sub AUTOLOAD {\n sub DESTROY { }\n \n \n+# Pipe implementation for ActiveState Perl.\n+\n+package Git::activestate_pipe;\n+use strict;\n+\n+sub TIEHANDLE {\n+\tmy ($class, @params) = @_;\n+\t# FIXME: This is probably horrible idea and the thing will explode\n+\t# at the moment you give it arguments that require some quoting,\n+\t# but I have no ActiveState clue... --pasky\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+\n+\n 1; # Famous last words\n"},{"id":"22504","messageId":"20060625015426.29906.54971.stgit@machine.or.cz","threadId":"4662","inReplyTo":"20060625015421.29906.50002.stgit@machine.or.cz","subject":"[PATCH 3/7] Git.pm: Swap hash_object() parameters","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-06-25T01:54:26Z","receivedAt":"2006-06-25T01:54:26Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"I'm about to introduce get_object() and it will be better for consistency\nif the object type always goes first. And writing 'blob' there explicitly\nis not much bother.\n\nSigned-off-by: Petr Baudis <pasky@suse.cz>\n---\n\n perl/Git.pm |    8 ++++----\n perl/Git.xs |    4 ++--\n 2 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/perl/Git.pm b/perl/Git.pm\nindex 6da11a6..eea6c07 100644\n--- a/perl/Git.pm\n+++ b/perl/Git.pm\n@@ -570,13 +570,13 @@ sub ident_person {\n \n \n \n-=item hash_object ( FILENAME [, TYPE ] )\n+=item hash_object ( TYPE, FILENAME )\n \n-=item hash_object ( FILEHANDLE [, TYPE ] )\n+=item hash_object ( TYPE, FILEHANDLE )\n \n Compute the SHA1 object id of the given C<FILENAME> (or data waiting in\n-C<FILEHANDLE>) considering it is of the C<TYPE> object type (C<blob>\n-(default), C<commit>, C<tree>).\n+C<FILEHANDLE>) considering it is of the C<TYPE> object type (C<blob>,\n+C<commit>, C<tree>).\n \n In case of C<FILEHANDLE> passed instead of file name, all the data\n available are read and hashed, and the filehandle is automatically\ndiff --git a/perl/Git.xs b/perl/Git.xs\nindex ebaac4b..2fd1c67 100644\n--- a/perl/Git.xs\n+++ b/perl/Git.xs\n@@ -105,9 +105,9 @@ CODE:\n }\n \n char *\n-xs_hash_object(file, type = \"blob\")\n-\tSV *file;\n+xs_hash_object(type, file)\n \tchar *type;\n+\tSV *file;\n CODE:\n {\n \tunsigned char sha1[20];\n"},{"id":"22506","messageId":"20060625015428.29906.66270.stgit@machine.or.cz","threadId":"4662","inReplyTo":"20060625015421.29906.50002.stgit@machine.or.cz","subject":"[PATCH 4/7] Git.pm: Fix Git->repository(\"/somewhere/totally/elsewhere\")","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-06-25T01:54:28Z","receivedAt":"2006-06-25T01:54:28Z","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 perl/Git.pm |    3 ++-\n 1 files changed, 2 insertions(+), 1 deletions(-)\n\ndiff --git a/perl/Git.pm b/perl/Git.pm\nindex eea6c07..1c3a2ec 100644\n--- a/perl/Git.pm\n+++ b/perl/Git.pm\n@@ -178,7 +178,8 @@ sub repository {\n \t\t};\n \n \t\tif ($dir) {\n-\t\t\t$opts{Repository} = abs_path($dir);\n+\t\t\t$dir =~ m#^/# or $dir = $opts{Directory} . '/' . $dir;\n+\t\t\t$opts{Repository} = $dir;\n \n \t\t\t# If --git-dir went ok, this shouldn't die either.\n \t\t\tmy $prefix = $search->command_oneline('rev-parse', '--show-prefix');\n"},{"id":"22508","messageId":"20060625015430.29906.5569.stgit@machine.or.cz","threadId":"4662","inReplyTo":"20060625015421.29906.50002.stgit@machine.or.cz","subject":"[PATCH 5/7] Make it possible to set up libgit directly (instead of from the environment)","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-06-25T01:54:30Z","receivedAt":"2006-06-25T01:54:30Z","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 efeafea..9844e88 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 946615d..6c4acc2 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 1c3a2ec..c490e0c 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@@ -822,11 +823,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 2fd1c67..4e85d69 100644\n--- a/perl/Git.xs\n+++ b/perl/Git.xs\n@@ -59,7 +59,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 const char *\ndiff --git a/sha1_file.c b/sha1_file.c\nindex c80528b..8d24f50 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 cd85d1f..1451cb6 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":"22509","messageId":"20060625015432.29906.31058.stgit@machine.or.cz","threadId":"4662","inReplyTo":"20060625015421.29906.50002.stgit@machine.or.cz","subject":"[PATCH 6/7] Git.pm: Introduce fast get_object() method","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-06-25T01:54:32Z","receivedAt":"2006-06-25T01:54:32Z","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 |   17 +++++++++++++++++\n perl/Git.xs |   24 ++++++++++++++++++++++++\n 2 files changed, 41 insertions(+), 0 deletions(-)\n\ndiff --git a/perl/Git.pm b/perl/Git.pm\nindex c490e0c..cdae1fe 100644\n--- a/perl/Git.pm\n+++ b/perl/Git.pm\n@@ -571,6 +571,23 @@ 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 \ndiff --git a/perl/Git.xs b/perl/Git.xs\nindex 4e85d69..e841e4a 100644\n--- a/perl/Git.xs\n+++ b/perl/Git.xs\n@@ -118,6 +118,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(type, file)\n \tchar *type;\n"},{"id":"22507","messageId":"20060625015434.29906.23422.stgit@machine.or.cz","threadId":"4662","inReplyTo":"20060625015421.29906.50002.stgit@machine.or.cz","subject":"[PATCH 7/7] Convert git-annotate to use Git.pm","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-06-25T01:54:34Z","receivedAt":"2006-06-25T01:54:34Z","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":"22510","messageId":"20060625015751.GB21864@pasky.or.cz","threadId":"4662","inReplyTo":"20060625015421.29906.50002.stgit@machine.or.cz","subject":"Re: [PATCH 1/7] Git.pm: Introduce ident() and ident_person() methods","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-06-25T01:57:51Z","receivedAt":"2006-06-25T01:57:51Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"> diff --git a/git-send-email.perl b/git-send-email.perl\n> index e794e44..79e82f5 100755\n> --- a/git-send-email.perl\n> +++ b/git-send-email.perl\n\nBTW, please tell me if you want to redo the patches without any script\nupdates (and how large portion of the patches to resend; my stg stack\nnow has 28 patches and I'm finally using it for some real workload!)\n- given that the plan is to have the converted scripts only in pu\n(or entirely outside your tree) but full-fledged Git.pm in tree.\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"},{"id":"22522","messageId":"7vhd29zu0j.fsf@assigned-by-dhcp.cox.net","threadId":"4662","inReplyTo":"20060625015751.GB21864@pasky.or.cz","subject":"Re: [PATCH 1/7] Git.pm: Introduce ident() and ident_person() methods","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-06-25T05:25:32Z","receivedAt":"2006-06-25T05:25:32Z","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>> diff --git a/git-send-email.perl b/git-send-email.perl\n>> index e794e44..79e82f5 100755\n>> --- a/git-send-email.perl\n>> +++ b/git-send-email.perl\n>\n> BTW, please tell me if you want to redo the patches without any script\n> updates (and how large portion of the patches to resend; my stg stack\n> now has 28 patches and I'm finally using it for some real workload!)\n> - given that the plan is to have the converted scripts only in pu\n> (or entirely outside your tree) but full-fledged Git.pm in tree.\n\nI'd avoid asking you to resend, but give me some time to see how\nthe series looks like first.\n"},{"id":"22529","messageId":"20060625092719.GH3154@h4x0r5.com","threadId":"4662","inReplyTo":"20060625015434.29906.23422.stgit@machine.or.cz","subject":"Re: [PATCH 7/7] Convert git-annotate to use Git.pm","fromName":"Ryan Anderson","fromEmail":"ryan@michonline.com","sentAt":"2006-06-25T09:27:23Z","receivedAt":"2006-06-25T09:27:23Z","isPatch":true,"sender":{"key":"ryan@michonline.com","avatar":null},"body":"On Sun, Jun 25, 2006 at 03:54:34AM +0200, Petr Baudis wrote:\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\nI don't see that Git.pm provides the compatibility functionality we have\nstubbed out, here.\n\n(It clearly has never been used, because there hasn't been a patch to\nset the actual ActiveState string there.. 'MSWin32', btw.)\n\nI suspect that the other scripts, that have more users, will see\nregression complaints when the qx() calls get replaced by calls to\nopen(my $fh, \"-|\"), indirectly, in Git.pm\n\n-- \n\nRyan Anderson\n  sometimes Pug Majere\n"},{"id":"22532","messageId":"20060625093618.GE21864@pasky.or.cz","threadId":"4662","inReplyTo":"20060625092719.GH3154@h4x0r5.com","subject":"Re: [PATCH 7/7] Convert git-annotate to use Git.pm","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-06-25T09:36:18Z","receivedAt":"2006-06-25T09:36:18Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Dear diary, on Sun, Jun 25, 2006 at 11:27:23AM CEST, I got a letter\nwhere Ryan Anderson <ryan@michonline.com> said that...\n> I don't see that Git.pm provides the compatibility functionality we have\n> stubbed out, here.\n\nPlease see [PATCH 2/7] Git.pm: Try to support ActiveState output pipe.\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"}]}