[RFC] git-remote

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



It might be handy to have a single command that helps you manage
your configuration that relates to downloading from remote
repositories.  This currently does only about 20% of what I want
it to do.

	$ git remote

shows the list of 'remotes' you have defined somewhere, and

	$ git remote origin

shows the details about the named remote (in this case
"origin").  How the branches are tracked, if you have a 
tracking branch that is stale, etc.

	$ git add another git://git.kernel.org/pub/...

defines the default remote.another.url and remote.another.fetch
entries just like a clone does; you can say "git fetch another"
afterwards.

For it to be useful, I think it should be enhanced to:

 - check overlaps of tracking branches and warn;

 - offer to remove stale tracking branches in one go;

 - offer ways to remove or rename remote;

 - give different levels of verbosity in its 'show' command,
   especially if ls-remote is run;

 - offer ways to update an existing remote, perhaps have an
   interactive mode;

Other enhancements might be also possible, but I do not think of
anything that is absolutely necessary other than the above right
now.

Signed-off-by: Junio C Hamano <junkio@xxxxxxx>
---
 Makefile        |    2 +-
 git-remote.perl |  260 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 261 insertions(+), 1 deletions(-)

diff --git a/Makefile b/Makefile
index fa1a022..ea07634 100644
--- a/Makefile
+++ b/Makefile
@@ -179,7 +179,7 @@ SCRIPT_SH = \
 SCRIPT_PERL = \
 	git-add--interactive.perl \
 	git-archimport.perl git-cvsimport.perl git-relink.perl \
-	git-cvsserver.perl \
+	git-cvsserver.perl git-remote.perl \
 	git-svnimport.perl git-cvsexportcommit.perl \
 	git-send-email.perl git-svn.perl
 
diff --git a/git-remote.perl b/git-remote.perl
new file mode 100755
index 0000000..b0ec838
--- /dev/null
+++ b/git-remote.perl
@@ -0,0 +1,260 @@
+#!/usr/bin/perl -w
+
+use Git;
+my $git = Git->repository();
+
+sub add_remote_config {
+	my ($hash, $name, $what, $value) = @_;
+	if ($what eq 'url') {
+		if (exists $hash->{$name}{'URL'}) {
+			print STDERR "Warning: more than one remote.$name.url\n";
+		}
+		$hash->{$name}{'URL'} = $value;
+	}
+	elsif ($what eq 'fetch') {
+		$hash->{$name}{'FETCH'} ||= [];
+		push @{$hash->{$name}{'FETCH'}}, $value;
+	}
+	if (!exists $hash->{$name}{'SOURCE'}) {
+		$hash->{$name}{'SOURCE'} = 'config';
+	}
+}
+
+sub add_remote_remotes {
+	my ($hash, $file, $name) = @_;
+
+	if (exists $hash->{$name}) {
+		$hash->{$name}{'WARNING'} = 'ignored due to config';
+		return;
+	}
+
+	my $fh;
+	if (!open($fh, '<', $file)) {
+		print STDERR "Warning: cannot open $file\n";
+		return;
+	}
+	my $it = { 'SOURCE' => 'remotes' };
+	$hash->{$name} = $it;
+	while (<$fh>) {
+		chomp;
+		if (/^URL:\s*(.*)$/) {
+			# Having more than one is Ok -- it is used for push.
+			if (! exists $it->{'URL'}) {
+				$it->{'URL'} = $1;
+			}
+		}
+		elsif (/^Push:\s*(.*)$/) {
+			; # later
+		}
+		elsif (/^Pull:\s*(.*)$/) {
+			$it->{'FETCH'} ||= [];
+			push @{$it->{'FETCH'}}, $1;
+		}
+		elsif (/^\#/) {
+			; # ignore
+		}
+		else {
+			print STDERR "Warning: funny line in $file: $_\n";
+		}
+	}
+	close($fh);
+}
+
+sub list_remote {
+	my ($git) = @_;
+	my %seen = ();
+
+	for ($git->command(qw(repo-config --get-regexp), '^remote\.')) {
+		if (/^remote\.([^.]*)\.(\S*)\s+(.*)$/) {
+			add_remote_config(\%seen, $1, $2, $3);
+		}
+	}
+
+	my $dir = $git->repo_path() . "/remotes";
+	if (opendir(my $dh, $dir)) {
+		local $_;
+		while ($_ = readdir($dh)) {
+			chomp;
+			next if (! -f "$dir/$_" || ! -r _);
+			add_remote_remotes(\%seen, "$dir/$_", $_);
+		}
+	}
+
+	return \%seen;
+}
+
+sub add_branch_config {
+	my ($hash, $name, $what, $value) = @_;
+	if ($what eq 'remote') {
+		if (exists $hash->{$name}{'REMOTE'}) {
+			print STDERR "Warning: more than one branch.$name.remote\n";
+		}
+		$hash->{$name}{'REMOTE'} = $value;
+	}
+	elsif ($what eq 'merge') {
+		$hash->{$name}{'MERGE'} ||= [];
+		push @{$hash->{$name}{'MERGE'}}, $value;
+	}
+}
+
+sub list_branch {
+	my ($git) = @_;
+	my %seen = ();
+	for ($git->command(qw(repo-config --get-regexp), '^branch\.')) {
+		if (/^branch\.([^.]*)\.(\S*)\s+(.*)$/) {
+			add_branch_config(\%seen, $1, $2, $3);
+		}
+	}
+
+	return \%seen;
+}
+
+my $remote = list_remote($git);
+my $branch = list_branch($git);
+
+sub update_ls_remote {
+	my ($harder, $info) = @_;
+
+	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'});
+	$info->{'LS_REMOTE'} = \@ref;
+}
+
+sub show_wildcard_mapping {
+	my ($forced, $ours, $ls) = @_;
+	my %refs;
+	for (@$ls) {
+		$refs{$_} = 01; # bit #0 to say "they have"
+	}
+	for ($git->command('for-each-ref', "refs/remotes/$ours")) {
+		chomp;
+		next unless (s|^[0-9a-f]{40}\s[a-z]+\srefs/remotes/$ours/||);
+		next if ($_ eq 'HEAD');
+		$refs{$_} ||= 0;
+		$refs{$_} |= 02; # bit #1 to say "we have"
+	}
+	my (@new, @stale, @tracked);
+	for (sort keys %refs) {
+		my $have = $refs{$_};
+		if ($have == 1) {
+			push @new, $_;
+		}
+		elsif ($have == 2) {
+			push @stale, $_;
+		}
+		elsif ($have == 3) {
+			push @tracked, $_;
+		}
+	}
+	if (@new) {
+		print "  New remote branches (next fetch will store in remotes/$ours)\n";
+		print "    @new\n";
+	}
+	if (@stale) {
+		print "  Stale tracking branches in remotes/$ours (you'd better remove them)\n";
+		print "    @stale\n";
+	}
+	if (@tracked) {
+		print "  Tracked remote branches\n";
+		print "    @tracked\n";
+	}
+}
+
+sub show_mapping {
+	my ($name, $info) = @_;
+	my $fetch = $info->{'FETCH'};
+	my $ls = $info->{'LS_REMOTE'};
+	my (@stale, @tracked);
+
+	for (@$fetch) {
+		next unless (/(\+)?([^:]+):(.*)/);
+		my ($forced, $theirs, $ours) = ($1, $2, $3);
+		if ($theirs eq 'refs/heads/*' &&
+		    $ours =~ /^refs\/remotes\/(.*)\/\*$/) {
+			# wildcard mapping
+			show_wildcard_mapping($forced, $1, $ls);
+		}
+		elsif ($theirs =~ /\*/ || $ours =~ /\*/) {
+			print STDERR "Warning: unrecognized mapping in remotes.$name.fetch: $_\n";
+		}
+		elsif ($theirs =~ s|^refs/heads/||) {
+			if (!grep { $_ eq $theirs } @$ls) {
+				push @stale, $theirs;
+			}
+			elsif ($ours ne '') {
+				push @tracked, $theirs;
+			}
+		}
+	}
+	if (@stale) {
+		print "  Stale tracking branches in remotes/$name (you'd better remove them)\n";
+		print "    @stale\n";
+	}
+	if (@tracked) {
+		print "  Tracked remote branches\n";
+		print "    @tracked\n";
+	}
+}
+
+sub show_remote {
+	my ($name, $ls_remote) = @_;
+	if (!exists $remote->{$name}) {
+		print STDERR "No such remote $name\n";
+		return;
+	}
+	my $info = $remote->{$name};
+	update_ls_remote($ls_remote, $info);
+
+	print "* remote $name\n";
+	print "  URL: $info->{'URL'}\n";
+	for my $branchname (sort keys %$branch) {
+		next if ($branch->{$branchname}{'REMOTE'} ne $name);
+		my @merged = map {
+			s|^refs/heads/||;
+			$_;
+		} split(' ',"@{$branch->{$branchname}{'MERGE'}}");
+		next unless (@merged);
+		print "  Remote branch(es) merged with 'git pull' while on branch $branchname\n";
+		print "    @merged\n";
+	}
+	if ($info->{'LS_REMOTE'}) {
+		show_mapping($name, $info);
+	}
+}
+
+sub add_remote {
+	my ($name, $url) = @_;
+	if (exists $remote->{$name}) {
+		print STDERR "remote $name already exists.\n";
+		exit(1);
+	}
+	$git->command('repo-config', "remote.$name.url", $url);
+	$git->command('repo-config', "remote.$name.fetch",
+		      "+refs/heads/*:refs/remotes/$name/*");
+}
+
+if (!@ARGV) {
+	for (sort keys %$remote) {
+		print "$_\n";
+	}
+}
+elsif ($ARGV[0] eq 'show') {
+	if (@ARGV != 2) {
+		print STDERR "Usage: git remote show <remote>\n";
+		exit(1);
+	}
+	show_remote($ARGV[1], 1);
+}
+elsif ($ARGV[0] eq 'add') {
+	if (@ARGV != 3) {
+		print STDERR "Usage: git remote show <name> <url>\n";
+		exit(1);
+	}
+	add_remote($ARGV[1], $ARGV[2]);
+}
+
-- 
1.5.0.rc0.gcc26


-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]