# [PATCH] Git.pm: Add remote_refs() git-ls-remote frontend

9 messages from 2007-08-25 to 2007-08-31. Participants: Petr Baudis, Junio C Hamano, Jakub Narebski.
Thread: https://gitlist.dev/t/9643

## Petr Baudis, 2007-08-25 22:11

Subject: [PATCH] Git.pm: Add remote_refs() git-ls-remote frontend
Message-ID: <20070825221143.6514.22516.stgit@rover>
URL: https://gitlist.dev/e/20070825221143.6514.22516.stgit%40rover

```
Should support all the important features, I guess. Too bad that
	git-ls-remote --heads .
	
is subtly different from

	git-ls-remote . refs/heads/

so we have to provide the interface for specifying both.

This patch also converts git-remote.perl to use it.

Signed-off-by: Petr Baudis <pasky@suse.cz>
---

 git-remote.perl |    5 +----
 perl/Git.pm     |   55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 55 insertions(+), 5 deletions(-)

diff --git a/git-remote.perl b/git-remote.perl
index 01cf480..8ce8418 100755
--- a/git-remote.perl
+++ b/git-remote.perl
@@ -128,10 +128,7 @@ sub update_ls_remote {
 	return if (($harder == 0) ||
 		   (($harder == 1) && exists $info->{'LS_REMOTE'}));
 
-	my @ref = map {
-		s|^[0-9a-f]{40}\s+refs/heads/||;
-		$_;
-	} $git->command(qw(ls-remote --heads), $info->{'URL'});
+	my @ref = keys %{$git->remote_refs($info->{'URL'}, [ 'heads' ])};
 	$info->{'LS_REMOTE'} = \@ref;
 }
 
diff --git a/perl/Git.pm b/perl/Git.pm
index 3f4080c..3bce733 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -51,7 +51,7 @@ require Exporter;
 # Methods which can be called as standalone functions as well:
 @EXPORT_OK = qw(command command_oneline command_noisy
                 command_output_pipe command_input_pipe command_close_pipe
-                version exec_path hash_object git_cmd_try);
+                version exec_path hash_object git_cmd_try remote_refs);
 
 
 =head1 DESCRIPTION
@@ -550,6 +550,59 @@ sub config_bool {
 }
 
 
+=item remote_refs ( REPOSITORY [, GROUPS [, REFGLOBS ] ] )
+
+This function returns a hashref of refs stored in a given remote repository.
+The hash is in the format C<refname =\> hash>. For tags, the C<refname> entry
+contains the tag object while a C<refname^{}> entry gives the tagged objects.
+
+C<REPOSITORY> has the same meaning as the appropriate C<git-ls-remote>
+argument; either an URL or a remote name (if called on a repository instance).
+C<GROUPS> is an optional arrayref that can contain 'tags' to return all the
+tags and/or 'heads' to return all the heads. C<REFGLOB> is an optional array
+of strings containing a shell-like glob to further limit the refs returned in
+the hash; the meaning is again the same as the appropriate C<git-ls-remote>
+argument.
+
+This function may or may not be called on a repository instance. In the former
+case, remote names as defined in the repository are recognized as repository
+specifiers.
+
+=cut
+
+sub remote_refs {
+	my ($self, $repo, $groups, $refglobs) = _maybe_self(@_);
+	my @args;
+	if (ref $groups eq 'ARRAY') {
+		foreach (@$groups) {
+			if ($_ eq 'heads') {
+				push (@args, '--heads');
+			} elsif ($_ eq 'tags') {
+				push (@args, '--tags');
+			} else {
+				# Ignore unknown groups for future
+				# compatibility
+			}
+		}
+	}
+	push (@args, $repo);
+	if (ref $refglobs eq 'ARRAY') {
+		push (@args, @$refglobs);
+	}
+
+	my @self = $self ? ($self) : (); # Ultra trickery
+	my ($fh, $ctx) = Git::command_output_pipe(@self, 'ls-remote', @args);
+	my %refs;
+	while (<$fh>) {
+		chomp;
+		my ($hash, $ref) = split(/\t/, $_, 2);
+		$refs{$ref} = $hash;
+	}
+	Git::command_close_pipe(@self, $fh, $ctx);
+	return \%refs;
+}
+
+
 =item ident ( TYPE | IDENTSTR )
 
 =item ident_person ( TYPE | IDENTSTR | IDENTARRAY )

```

## Junio C Hamano, 2007-08-26 04:26

Subject: Re: [PATCH] Git.pm: Add remote_refs() git-ls-remote frontend
Message-ID: <7v6432udje.fsf@gitster.siamese.dyndns.org>
URL: https://gitlist.dev/e/7v6432udje.fsf%40gitster.siamese.dyndns.org
In-Reply-To: <20070825221143.6514.22516.stgit@rover>

```
Petr Baudis <pasky@suse.cz> writes:

> diff --git a/git-remote.perl b/git-remote.perl
> index 01cf480..8ce8418 100755
> --- a/git-remote.perl
> +++ b/git-remote.perl
> @@ -128,10 +128,7 @@ sub update_ls_remote {
>  	return if (($harder == 0) ||
>  		   (($harder == 1) && exists $info->{'LS_REMOTE'}));
>  
> -	my @ref = map {
> -		s|^[0-9a-f]{40}\s+refs/heads/||;
> -		$_;
> -	} $git->command(qw(ls-remote --heads), $info->{'URL'});
> +	my @ref = keys %{$git->remote_refs($info->{'URL'}, [ 'heads' ])};
>  	$info->{'LS_REMOTE'} = \@ref;
>  }

IIRC, ls-remote returns refs sorted and this function returns
them as it receives.

Doesn't this change make @ref randomly ordered?  

```

## Petr Baudis, 2007-08-26 09:58

Subject: Re: [PATCH] Git.pm: Add remote_refs() git-ls-remote frontend
Message-ID: <20070826095836.GG1219@pasky.or.cz>
URL: https://gitlist.dev/e/20070826095836.GG1219%40pasky.or.cz
In-Reply-To: <7v6432udje.fsf@gitster.siamese.dyndns.org>

```
On Sun, Aug 26, 2007 at 06:26:29AM CEST, Junio C Hamano wrote:
> Petr Baudis <pasky@suse.cz> writes:
> 
> > diff --git a/git-remote.perl b/git-remote.perl
> > index 01cf480..8ce8418 100755
> > --- a/git-remote.perl
> > +++ b/git-remote.perl
> > @@ -128,10 +128,7 @@ sub update_ls_remote {
> >  	return if (($harder == 0) ||
> >  		   (($harder == 1) && exists $info->{'LS_REMOTE'}));
> >  
> > -	my @ref = map {
> > -		s|^[0-9a-f]{40}\s+refs/heads/||;
> > -		$_;
> > -	} $git->command(qw(ls-remote --heads), $info->{'URL'});
> > +	my @ref = keys %{$git->remote_refs($info->{'URL'}, [ 'heads' ])};
> >  	$info->{'LS_REMOTE'} = \@ref;
> >  }
> 
> IIRC, ls-remote returns refs sorted and this function returns
> them as it receives.
> 
> Doesn't this change make @ref randomly ordered?  

Yes, but I couldn't find any place in git-remote.perl where we rely on
the @ref ordering - maybe I'm blind...

-- 
				Petr "Pasky" Baudis
Ever try. Ever fail. No matter. // Try again. Fail again. Fail better.
		-- Samuel Beckett

```

## Jakub Narebski, 2007-08-30 08:39

Subject: Re: [PATCH] Git.pm: Add remote_refs() git-ls-remote frontend
Message-ID: <fb5vod$vvc$1@sea.gmane.org>
URL: https://gitlist.dev/e/fb5vod%24vvc%241%40sea.gmane.org
In-Reply-To: <20070825221143.6514.22516.stgit@rover>

```
[Cc: Petr Baudis <pasky@suse.cz>, git@vger.kernel.org]

Petr Baudis wrote:

> Should support all the important features, I guess. Too bad that
>       git-ls-remote --heads .
>       
> is subtly different from
> 
>       git-ls-remote . refs/heads/
> 
> so we have to provide the interface for specifying both.

Why do not use git-for-each-ref or git-show-refs? If I remember correctly
they _were_ faster than git-ls-remote or git-peek-remote...

-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git

```

## Petr Baudis, 2007-08-30 23:37

Subject: Re: [PATCH] Git.pm: Add remote_refs() git-ls-remote frontend
Message-ID: <20070830233714.GJ1219@pasky.or.cz>
URL: https://gitlist.dev/e/20070830233714.GJ1219%40pasky.or.cz
In-Reply-To: <fb5vod$vvc$1@sea.gmane.org>

```
On Thu, Aug 30, 2007 at 10:39:41AM CEST, Jakub Narebski wrote:
> [Cc: Petr Baudis <pasky@suse.cz>, git@vger.kernel.org]
> 
> Petr Baudis wrote:
> 
> > Should support all the important features, I guess. Too bad that
> >       git-ls-remote --heads .
> >       
> > is subtly different from
> > 
> >       git-ls-remote . refs/heads/
> > 
> > so we have to provide the interface for specifying both.
> 
> Why do not use git-for-each-ref or git-show-refs? If I remember correctly
> they _were_ faster than git-ls-remote or git-peek-remote...

But we want to typically list refs in a remote repository.

-- 
				Petr "Pasky" Baudis
Early to rise and early to bed makes a male healthy and wealthy and dead.
                -- James Thurber

```

## Petr Baudis, 2007-08-30 23:37

Subject: [PATCH] Git.pm: Add remote_refs() git-ls-remote frontend
Message-ID: <20070830233749.4480.72281.stgit@rover>
URL: https://gitlist.dev/e/20070830233749.4480.72281.stgit%40rover
In-Reply-To: <20070825221143.6514.22516.stgit@rover>

```
Should support all the important features, I guess. Too bad that
	git-ls-remote --heads .
	
is subtly different from

	git-ls-remote . refs/heads/

so we have to provide the interface for specifying both.

This patch also converts git-remote.perl to use it.

Signed-off-by: Petr Baudis <pasky@suse.cz>
---
This version re-adds missed out stripping of refs/heads/ in git-remote.perl.
---

 git-remote.perl |    5 +----
 perl/Git.pm     |   55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 55 insertions(+), 5 deletions(-)

diff --git a/git-remote.perl b/git-remote.perl
index 01cf480..1948c5c 100755
--- a/git-remote.perl
+++ b/git-remote.perl
@@ -128,10 +128,7 @@ sub update_ls_remote {
 	return if (($harder == 0) ||
 		   (($harder == 1) && exists $info->{'LS_REMOTE'}));
 
-	my @ref = map {
-		s|^[0-9a-f]{40}\s+refs/heads/||;
-		$_;
-	} $git->command(qw(ls-remote --heads), $info->{'URL'});
+	my @ref = map { s|refs/heads/||; $_; } keys %{$git->remote_refs($info->{'URL'}, [ 'heads' ])};
 	$info->{'LS_REMOTE'} = \@ref;
 }
 
diff --git a/perl/Git.pm b/perl/Git.pm
index 3f4080c..3bce733 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -51,7 +51,7 @@ require Exporter;
 # Methods which can be called as standalone functions as well:
 @EXPORT_OK = qw(command command_oneline command_noisy
                 command_output_pipe command_input_pipe command_close_pipe
-                version exec_path hash_object git_cmd_try);
+                version exec_path hash_object git_cmd_try remote_refs);
 
 
 =head1 DESCRIPTION
@@ -550,6 +550,59 @@ sub config_bool {
 }
 
 
+=item remote_refs ( REPOSITORY [, GROUPS [, REFGLOBS ] ] )
+
+This function returns a hashref of refs stored in a given remote repository.
+The hash is in the format C<refname =\> hash>. For tags, the C<refname> entry
+contains the tag object while a C<refname^{}> entry gives the tagged objects.
+
+C<REPOSITORY> has the same meaning as the appropriate C<git-ls-remote>
+argument; either an URL or a remote name (if called on a repository instance).
+C<GROUPS> is an optional arrayref that can contain 'tags' to return all the
+tags and/or 'heads' to return all the heads. C<REFGLOB> is an optional array
+of strings containing a shell-like glob to further limit the refs returned in
+the hash; the meaning is again the same as the appropriate C<git-ls-remote>
+argument.
+
+This function may or may not be called on a repository instance. In the former
+case, remote names as defined in the repository are recognized as repository
+specifiers.
+
+=cut
+
+sub remote_refs {
+	my ($self, $repo, $groups, $refglobs) = _maybe_self(@_);
+	my @args;
+	if (ref $groups eq 'ARRAY') {
+		foreach (@$groups) {
+			if ($_ eq 'heads') {
+				push (@args, '--heads');
+			} elsif ($_ eq 'tags') {
+				push (@args, '--tags');
+			} else {
+				# Ignore unknown groups for future
+				# compatibility
+			}
+		}
+	}
+	push (@args, $repo);
+	if (ref $refglobs eq 'ARRAY') {
+		push (@args, @$refglobs);
+	}
+
+	my @self = $self ? ($self) : (); # Ultra trickery
+	my ($fh, $ctx) = Git::command_output_pipe(@self, 'ls-remote', @args);
+	my %refs;
+	while (<$fh>) {
+		chomp;
+		my ($hash, $ref) = split(/\t/, $_, 2);
+		$refs{$ref} = $hash;
+	}
+	Git::command_close_pipe(@self, $fh, $ctx);
+	return \%refs;
+}
+
+
 =item ident ( TYPE | IDENTSTR )
 
 =item ident_person ( TYPE | IDENTSTR | IDENTARRAY )

```

## Junio C Hamano, 2007-08-31 00:26

Subject: Re: [PATCH] Git.pm: Add remote_refs() git-ls-remote frontend
Message-ID: <7vr6lk5z2p.fsf@gitster.siamese.dyndns.org>
URL: https://gitlist.dev/e/7vr6lk5z2p.fsf%40gitster.siamese.dyndns.org
In-Reply-To: <20070830233749.4480.72281.stgit@rover>

```
Petr Baudis <pasky@suse.cz> writes:

> Should support all the important features, I guess. Too bad that
>
> 	git-ls-remote --heads .
> 	
> is subtly different from
>
> 	git-ls-remote . refs/heads/
>
> so we have to provide the interface for specifying both.

Are they subtly different?  To me they look like asking for
completely different things.  IIRC, the patterns are ref globs,
so

	git ls-remote $URL 'refs/heads/*'

would be the same as the former one.

> This patch also converts git-remote.perl to use it.
>
> Signed-off-by: Petr Baudis <pasky@suse.cz>
> ---
> This version re-adds missed out stripping of refs/heads/ in git-remote.perl.

Ah, that was the bug you were talking about on #git channel, I
guess...

```

## Petr Baudis, 2007-08-31 01:37

Subject: Re: [PATCH] Git.pm: Add remote_refs() git-ls-remote frontend
Message-ID: <20070831013700.GL1219@pasky.or.cz>
URL: https://gitlist.dev/e/20070831013700.GL1219%40pasky.or.cz
In-Reply-To: <7vr6lk5z2p.fsf@gitster.siamese.dyndns.org>

```
On Fri, Aug 31, 2007 at 02:26:22AM CEST, Junio C Hamano wrote:
> Petr Baudis <pasky@suse.cz> writes:
> 
> > Should support all the important features, I guess. Too bad that
> >
> > 	git-ls-remote --heads .
> > 	
> > is subtly different from
> >
> > 	git-ls-remote . refs/heads/
> >
> > so we have to provide the interface for specifying both.
> 
> Are they subtly different?  To me they look like asking for
> completely different things.  IIRC, the patterns are ref globs,
> so
> 
> 	git ls-remote $URL 'refs/heads/*'
> 
> would be the same as the former one.

Yes, though subtly different, as I've already explained before - for the
case of say

	refs/remotes/evil/refs/heads

> Ah, that was the bug you were talking about on #git channel, I
> guess...

*nod*

-- 
				Petr "Pasky" Baudis
Early to rise and early to bed makes a male healthy and wealthy and dead.
                -- James Thurber

```

## Junio C Hamano, 2007-08-31 02:13

Subject: Re: [PATCH] Git.pm: Add remote_refs() git-ls-remote frontend
Message-ID: <7vir6w5u4c.fsf@gitster.siamese.dyndns.org>
URL: https://gitlist.dev/e/7vir6w5u4c.fsf%40gitster.siamese.dyndns.org
In-Reply-To: <20070831013700.GL1219@pasky.or.cz>

```
Petr Baudis <pasky@suse.cz> writes:

>> 	git ls-remote $URL 'refs/heads/*'
>> 
>> would be the same as the former one.
>
> Yes, though subtly different, as I've already explained before - for the
> case of say
>
> 	refs/remotes/evil/refs/heads

Are you voting for removal of "ls-remote $URL 'heads/*'" support?
I actually think removing it makes a perfect sense.

>> Ah, that was the bug you were talking about on #git channel, I
>> guess...
>
> *nod*

```
