Re: [PATCH] git-svn: teach to create and operate on bare repositories

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

 



Eric, good day.

Tue, Apr 21, 2009 at 11:01:01AM -0700, Eric Wong wrote:
> This definitely seems useful.  I'd like a basic test to ensure it
> continues working in the future.

No problems, answers inline ;))

> >  sub cmd_set_tree {
> >  	my (@commits) = @_;
> > +
> > +	get_bareness();
> > +	if ($_bare) {
> > +		fatal("'set-tree' isn't supported for bare repositories.");
> > +	}
> > +
> 
> This repetition with get_bareness() and then checking for $_bare all
> over the place bothers me a bit.  I'd rather just have something like
> this:
> 
> 	fatal_if_bare('set-tree');
> 
> Or even:
> 
> 	fatal_if_bare($cmd);

Done.

> > +# Initialize bareness flag for an existing repository
> > +sub get_bareness {
> > +	return unless -d $ENV{GIT_DIR};
> > +	my $result = Git::config_bool('core.bare');
> > +	$result = 0 unless defined($result);
> > +	$_bare = $result;
> > +}
> 
> Perhaps this function can go into Git.pm since core.bare affects all
> of git, not just git-svn.  I'd also like to just use something like
> 
> 	if (Git::config_bare()) { ... }
> 
> ...rather than having to check/initialize a variable every time.
> 
> Of course, we can have config caching for platforms that really need
> it done via Sam's Git::Config module when it gets merged.

Hmm, for my needs the bare "Git::config_bool('core.bare')" is sufficient,
so I implanted it everywhere.

> >  sub extract_metadata {
> >  	my $id = shift or return (undef, undef, undef);
> >  	my ($url, $rev, $uuid) = ($id =~ /^\s*git-svn-id:\s+(.*)\@(\d+)
> > @@ -1527,6 +1588,51 @@ sub parse_revision_argument {
> >  	die "revision argument: $::_revision not understood by git-svn\n";
> >  }
> >  
> > +#
> > +# While we are fetching to bare repositories, we should update branch
> > +# heads manually, because it is not possible to do merges within bare
> > +# repositories.
> > +#
> > +# Arguments:
> > +# - name of remote branch, for example 'refs/remotes/git-svn';
> > +# - name of the corresponding local head, for example 'refs/heads/master'.
> > +#
> > +sub fast_forward_bare_fetch {
> > +	return unless defined($_bare);
> > +	return unless defined($_[0]);
> > +	return unless defined($_[1]);
> 
> Checking for arguments here seems unnecessarily defensive.

I used to this style, but OK, it was eliminated.

> 
> > +	my $remote = $_[0];
> > +	my $localhead = $_[1];
> 
> The general git-svn style is this:
> 
> 	my ($remote, $localhead) = @_;

Fixed.

> > +	# Update (fast-forward) heads for bare repository.
> > +	if (defined($_bare)) {
> > +		foreach my $gs (@gs) {
> > +			my $remote = 'refs/remotes/' . $gs->{ref_id};
> > +			my $localhead = 'refs/heads/master';
> > +			if (open(FH, "<", "HEAD")) {
> > +				chomp(my $head = <FH>);
> > +				close FH;
> > +				if ($head =~ /^ref: (.*)$/) {
> > +					$localhead = $1;
> > +				}
> > +			}
> 
> "git rev-parse --symbolic-full-name HEAD" should work better
> than parsing HEAD ourselves.

Used it.  The resulting patch is attached.

I have one question: as you can see from the above hunk, I am using HEAD
as the tip of all local branches to be fast-forwarded.  It works with
only one upstream branch, but what if there will be multiple
(disconnected) remotes we are tracking?  I had seen the words that it it
possible (in the git-svn man page), but hadn't managed to create such
repository and figure out how can I deduce the name of the local head
for the remote one.  Could you, please, enlighten me?

Thanks!
-- 
Eygene
From cc509782cfa3c17dc830015891cde2a11275a2c4 Mon Sep 17 00:00:00 2001
From: Eygene Ryabinkin <rea@xxxxxxxxxxx>
Date: Thu, 16 Apr 2009 02:27:24 +0400
Subject: [PATCH] git-svn: teach to create and operate on bare repositories

This mode is useful when we're mirrorring one-to-one Subversion and Git
repositories and then using Git as the repository to base other work on.

Bare mode is currently restricted only to init, clone, fetch, log,
find-rev, show-ignore, show-externals, create-ignore, propget, proplist
and info commands.  Others won't work at all and error message will
be produced.

Signed-off-by: Eygene Ryabinkin <rea-git@xxxxxxxxxxx>
---
 Documentation/git-svn.txt |   23 +++++++++
 git-svn.perl              |  110 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 129 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 9229d45..4749676 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -104,6 +104,10 @@ COMMANDS
 --parent;;
 	Fetch only from the SVN parent of the current HEAD.
 
+--bare;;
+	Creates bare repository without checked-out copy.  See section
+	BARE REPOSITORIES for details.
+
 This doesn't interfere with interoperating with the Subversion
 repository you cloned from, but if you wish for your local Git
 repository to be able to interoperate with someone else's local Git
@@ -537,6 +541,25 @@ and these settings should never be changed once they are set.
 Additionally, only one of these four options can be used per-svn-remote
 section because they affect the 'git-svn-id:' metadata line.
 
+BARE REPOSITORIES
+-----------------
+
+Bare repositories are most suitable for serving them via 'git-daemon',
+so they can be used when one wants to convert Subversion repository to
+Git and base the work on the created Git repository.  This was the
+initial idea of adding support for such repositories: repository is
+thought to be read-only for Git clients, but could be updated via 'git
+svn fetch' to catch changes in Subversion repository.
+
+Only subset of all 'git svn' commands are available for bare
+repositories: init, clone, fetch, log, find-rev, show-ignore,
+show-externals, create-ignore, propget, proplist and info.
+
+One certainly could push to bare repository that is maintained by
+'git-svn', but merges are not supported on the bare repositories, so it
+could be hard to recover from such push.  The bottom line: currently
+supported mode is read-only for all Git clients and only this mode is
+now tested and supported.  Git wizards are, of course, free for all.
 
 BASIC EXAMPLES
 --------------
diff --git a/git-svn.perl b/git-svn.perl
index c5965c9..db6037f 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -63,13 +63,14 @@ $sha1 = qr/[a-f\d]{40}/;
 $sha1_short = qr/[a-f\d]{4,40}/;
 my ($_stdin, $_help, $_edit,
 	$_message, $_file,
-	$_template, $_shared,
+	$_template, $_shared, $_bare,
 	$_version, $_fetch_all, $_no_rebase, $_fetch_parent,
 	$_merge, $_strategy, $_dry_run, $_local,
 	$_prefix, $_no_checkout, $_url, $_verbose,
 	$_git_format, $_commit_url, $_tag);
 $Git::SVN::_follow_parent = 1;
 $_q ||= 0;
+$_bare = 0;
 my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
                     'config-dir=s' => \$Git::SVN::Ra::config_dir,
                     'no-auth-cache' => \$Git::SVN::Prompt::_no_auth_cache,
@@ -96,6 +97,7 @@ my %init_opts = ( 'template=s' => \$_template, 'shared:s' => \$_shared,
                   'trunk|T=s' => \$_trunk, 'tags|t=s' => \$_tags,
                   'branches|b=s' => \$_branches, 'prefix=s' => \$_prefix,
                   'stdlayout|s' => \$_stdlayout,
+                  'bare' => \$_bare,
                   'minimize-url|m' => \$Git::SVN::_minimize_url,
 		  'no-metadata' => sub { $icv{noMetadata} = 1 },
 		  'use-svm-props' => sub { $icv{useSvmProps} = 1 },
@@ -315,7 +317,7 @@ sub version {
 }
 
 sub do_git_init_db {
-	unless (-d $ENV{GIT_DIR}) {
+	unless (-d $ENV{GIT_DIR} && !$_bare) {
 		my @init_db = ('init');
 		push @init_db, "--template=$_template" if defined $_template;
 		if (defined $_shared) {
@@ -325,8 +327,15 @@ sub do_git_init_db {
 				push @init_db, "--shared";
 			}
 		}
+		push @init_db, "--bare" if $_bare;
 		command_noisy(@init_db);
-		$_repository = Git->repository(Repository => ".git");
+		if ($_bare && -d $ENV{GIT_DIR}) {
+			$_repository =
+			    Git->repository(Repository => $ENV{GIT_DIR});
+		} else {
+			$_repository =
+			    Git->repository(Repository => ".git");
+		}
 	}
 	command_noisy('config', 'core.autocrlf', 'false');
 	my $set;
@@ -344,9 +353,20 @@ sub do_git_init_db {
 
 sub init_subdir {
 	my $repo_path = shift or return;
+	my $full_path;
+	if ($repo_path =~ /^\//) {
+		$full_path = $repo_path;
+	} else {
+		use POSIX qw(getcwd);
+		$full_path = POSIX::getcwd() . "/" . $repo_path;
+	}
 	mkpath([$repo_path]) unless -d $repo_path;
 	chdir $repo_path or die "Couldn't chdir to $repo_path: $!\n";
-	$ENV{GIT_DIR} = '.git';
+	if (!$_bare) {
+		$ENV{GIT_DIR} = $full_path . '/.git';
+	} else {
+		$ENV{GIT_DIR} = $full_path;
+	}
 	$_repository = Git->repository(Repository => $ENV{GIT_DIR});
 }
 
@@ -408,6 +428,9 @@ sub cmd_fetch {
 
 sub cmd_set_tree {
 	my (@commits) = @_;
+
+	fatal_if_bare($cmd);
+
 	if ($_stdin || !@commits) {
 		print "Reading from stdin...\n";
 		@commits = ();
@@ -444,6 +467,9 @@ sub cmd_set_tree {
 
 sub cmd_dcommit {
 	my $head = shift;
+
+	fatal_if_bare($cmd);
+
 	git_cmd_try { command_oneline(qw/diff-index --quiet HEAD/) }
 		'Cannot dcommit with a dirty index.  Commit your changes first, '
 		. "or stash them with `git stash'.\n";
@@ -579,6 +605,8 @@ sub cmd_dcommit {
 sub cmd_branch {
 	my ($branch_name, $head) = @_;
 
+	fatal_if_bare($cmd);
+
 	unless (defined $branch_name && length $branch_name) {
 		die(($_tag ? "tag" : "branch") . " name required\n");
 	}
@@ -635,6 +663,8 @@ sub cmd_find_rev {
 }
 
 sub cmd_rebase {
+	fatal_if_bare($cmd);
+
 	command_noisy(qw/update-index --refresh/);
 	my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
 	unless ($gs) {
@@ -793,6 +823,8 @@ sub cmd_proplist {
 }
 
 sub cmd_multi_init {
+	fatal_if_bare($cmd);
+
 	my $url = shift;
 	unless (defined $_trunk || defined $_branches || defined $_tags) {
 		usage(1);
@@ -836,6 +868,8 @@ sub cmd_multi_fetch {
 
 # this command is special because it requires no metadata
 sub cmd_commit_diff {
+	fatal_if_bare($cmd);
+
 	my ($ta, $tb, $url) = @_;
 	my $usage = "Usage: $0 commit-diff -r<revision> ".
 	            "<tree-ish> <tree-ish> [<URL>]";
@@ -1527,6 +1561,57 @@ sub parse_revision_argument {
 	die "revision argument: $::_revision not understood by git-svn\n";
 }
 
+#
+# While we are fetching to bare repositories, we should update branch
+# heads manually, because it is not possible to do merges within bare
+# repositories.
+#
+# Arguments:
+# - name of remote branch, for example 'refs/remotes/git-svn';
+# - name of the corresponding local head, for example 'refs/heads/master'.
+#
+sub fast_forward_bare_fetch {
+	return unless Git::config_bool('core.bare');
+	my ($remote, $localhead) = @_;
+
+	# Gather current SHA1 codes for both objects.
+	my $new_sha1ref = eval {
+		command_oneline(qw/show-ref --hash/, $remote)
+	};
+	return unless length($new_sha1ref);
+	my $current_sha1ref = eval {
+		command_oneline(qw/show-ref --hash/, $localhead)
+	};
+	return unless length($current_sha1ref);
+
+	# Same?  Nothing to do.
+	return if $new_sha1ref eq $current_sha1ref;
+
+	# Update SHA1 for local head and verify new value.
+	command_noisy('update-ref', $localhead, $new_sha1ref);
+	$current_sha1ref = eval {
+		command_oneline(qw/show-ref --hash/, $localhead)
+	};
+	if ($current_sha1ref eq $new_sha1ref) {
+		unless ($::_q > 1) {
+			printf "Fast-forwarded %s to %s.\n",
+			  $localhead, $new_sha1ref;
+		}
+	} else {
+		printf "Error fast forwarding %s to %s.\n",
+		  $localhead, $new_sha1ref;
+	}
+}
+
+sub fatal_if_bare {
+	my ($cmd) = @_;
+
+	if (!Git::config_bool('core.bare')) {
+		fatal(sprintf("'%s' isn't supported for bare repositories.",
+		  $cmd));
+	}
+}
+
 sub fetch_all {
 	my ($repo_id, $remotes) = @_;
 	if (ref $repo_id) {
@@ -1571,6 +1656,23 @@ sub fetch_all {
 
 	($base, $head) = parse_revision_argument($base, $head);
 	$ra->gs_fetch_loop_common($base, $head, \@gs, \@globs);
+
+	# Update (fast-forward) heads for bare repository.
+	if (Git::config_bool('core.bare')) {
+		foreach my $gs (@gs) {
+			my $remote = 'refs/remotes/' . $gs->{ref_id};
+			my $localhead = eval {
+			  command_oneline(qw/rev-parse
+			  --symbolic-full-name HEAD/)
+			};
+			if (length($localhead) == 0 ||
+			    $localhead eq "HEAD") {
+				die(sprintf("Unable to dereference HEAD " .
+				  "for %s"), $remote);
+			}
+			fast_forward_bare_fetch($remote, $localhead);
+		}
+	}
 }
 
 sub read_all_remotes {
-- 
1.6.2.4


[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]