git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH] contrib/git-svn: stabilize memory usage for big fetches

From
EWEric Wong <normalperson@yhbt.net>
Date
Mar 26, 2006, 02:52 UTC
Message-ID
<11433415513822-git-send-email-normalperson@yhbt.net>
In-Reply-To
<20060325082521.GA17473@hand.yhbt.net>

We should be safely able to import histories with thousands of revisions without hogging up lots of memory.

With this, we lose the ability to autocorrect mistakes when people specify revisions in reverse, but it's probably no longer a problem since we only have one method of log parsing nowadays.

I've added an extra check to ensure that revision numbers do increment.

Also, increment the version number to 0.11.0. I really should just call it 1.0 soon...

Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
 contrib/git-svn/git-svn.perl |  109 ++++++++++++++++++++++++------------------
 1 files changed, 63 insertions(+), 46 deletions(-)
c76df6617116a7d330a3110230bc3b01eaf9c66d
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index f3fc3ec..3e5733e 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -8,7 +8,7 @@ use vars qw/	$AUTHOR $VERSION
 		$GIT_SVN_INDEX $GIT_SVN
 		$GIT_DIR $REV_DIR/;
 $AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
-$VERSION = '0.10.0';
+$VERSION = '0.11.0';
 $GIT_DIR = $ENV{GIT_DIR} || "$ENV{PWD}/.git";
 # make sure the svn binary gives consistent output between locales and TZs:
 $ENV{TZ} = 'UTC';
@@ -217,9 +217,8 @@ sub fetch {
 	push @log_args, '--stop-on-copy' unless $_no_stop_copy;
 
 	my $svn_log = svn_log_raw(@log_args);
-	@$svn_log = sort { $a->{revision} <=> $b->{revision} } @$svn_log;
 
-	my $base = shift @$svn_log or croak "No base revision!\n";
+	my $base = next_log_entry($svn_log) or croak "No base revision!\n";
 	my $last_commit = undef;
 	unless (-d $SVN_WC) {
 		svn_cmd_checkout($SVN_URL,$base->{revision},$SVN_WC);
@@ -234,18 +233,22 @@ sub fetch {
 	}
 	my @svn_up = qw(svn up);
 	push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
-	my $last_rev = $base->{revision};
-	foreach my $log_msg (@$svn_log) {
-		assert_svn_wc_clean($last_rev, $last_commit);
-		$last_rev = $log_msg->{revision};
-		sys(@svn_up,"-r$last_rev");
+	my $last = $base;
+	while (my $log_msg = next_log_entry($svn_log)) {
+		assert_svn_wc_clean($last->{revision}, $last_commit);
+		if ($last->{revision} >= $log_msg->{revision}) {
+			croak "Out of order: last >= current: ",
+				"$last->{revision} >= $log_msg->{revision}\n";
+		}
+		sys(@svn_up,"-r$log_msg->{revision}");
 		$last_commit = git_commit($log_msg, $last_commit, @parents);
+		$last = $log_msg;
 	}
-	assert_svn_wc_clean($last_rev, $last_commit);
+	assert_svn_wc_clean($last->{revision}, $last_commit);
 	unless (-e "$GIT_DIR/refs/heads/master") {
 		sys(qw(git-update-ref refs/heads/master),$last_commit);
 	}
-	return pop @$svn_log;
+	return $last;
 }
 
 sub commit {
@@ -708,49 +711,61 @@ sub svn_commit_tree {
 	return fetch("$rev_committed=$commit")->{revision};
 }
 
+# read the entire log into a temporary file (which is removed ASAP)
+# and store the file handle + parser state
 sub svn_log_raw {
 	my (@log_args) = @_;
-	my $pid = open my $log_fh,'-|';
+	my $log_fh = IO::File->new_tmpfile or croak $!;
+	my $pid = fork;
 	defined $pid or croak $!;
-
-	if ($pid == 0) {
+	if (!$pid) {
+		open STDOUT, '>&', $log_fh or croak $!;
 		exec (qw(svn log), @log_args) or croak $!
 	}
+	waitpid $pid, 0;
+	croak if $?;
+	seek $log_fh, 0, 0 or croak $!;
+	return { state => 'sep', fh => $log_fh };
+}
+
+sub next_log_entry {
+	my $log = shift; # retval of svn_log_raw()
+	my $ret = undef;
+	my $fh = $log->{fh};
 
-	my @svn_log;
-	my $state = 'sep';
-	while (<$log_fh>) {
+	while (<$fh>) {
 		chomp;
 		if (/^\-{72}$/) {
-			if ($state eq 'msg') {
-				if ($svn_log[$#svn_log]->{lines}) {
-					$svn_log[$#svn_log]->{msg} .= $_."\n";
-					unless(--$svn_log[$#svn_log]->{lines}) {
-						$state = 'sep';
+			if ($log->{state} eq 'msg') {
+				if ($ret->{lines}) {
+					$ret->{msg} .= $_."\n";
+					unless(--$ret->{lines}) {
+						$log->{state} = 'sep';
 					}
 				} else {
 					croak "Log parse error at: $_\n",
-						$svn_log[$#svn_log]->{revision},
+						$ret->{revision},
 						"\n";
 				}
 				next;
 			}
-			if ($state ne 'sep') {
+			if ($log->{state} ne 'sep') {
 				croak "Log parse error at: $_\n",
-					"state: $state\n",
-					$svn_log[$#svn_log]->{revision},
+					"state: $log->{state}\n",
+					$ret->{revision},
 					"\n";
 			}
-			$state = 'rev';
+			$log->{state} = 'rev';
 
 			# if we have an empty log message, put something there:
-			if (@svn_log) {
-				$svn_log[$#svn_log]->{msg} ||= "\n";
-				delete $svn_log[$#svn_log]->{lines};
+			if ($ret) {
+				$ret->{msg} ||= "\n";
+				delete $ret->{lines};
+				return $ret;
 			}
 			next;
 		}
-		if ($state eq 'rev' && s/^r(\d+)\s*\|\s*//) {
+		if ($log->{state} eq 'rev' && s/^r(\d+)\s*\|\s*//) {
 			my $rev = $1;
 			my ($author, $date, $lines) = split(/\s*\|\s*/, $_, 3);
 			($lines) = ($lines =~ /(\d+)/);
@@ -758,36 +773,34 @@ sub svn_log_raw {
 					/(\d{4})\-(\d\d)\-(\d\d)\s
 					 (\d\d)\:(\d\d)\:(\d\d)\s([\-\+]\d+)/x)
 					 or croak "Failed to parse date: $date\n";
-			my %log_msg = (	revision => $rev,
+			$ret = {	revision => $rev,
 					date => "$tz $Y-$m-$d $H:$M:$S",
 					author => $author,
 					lines => $lines,
-					msg => '' );
+					msg => '' };
 			if (defined $_authors && ! defined $users{$author}) {
 				die "Author: $author not defined in ",
 						"$_authors file\n";
 			}
-			push @svn_log, \%log_msg;
-			$state = 'msg_start';
+			$log->{state} = 'msg_start';
 			next;
 		}
 		# skip the first blank line of the message:
-		if ($state eq 'msg_start' && /^$/) {
-			$state = 'msg';
-		} elsif ($state eq 'msg') {
-			if ($svn_log[$#svn_log]->{lines}) {
-				$svn_log[$#svn_log]->{msg} .= $_."\n";
-				unless (--$svn_log[$#svn_log]->{lines}) {
-					$state = 'sep';
+		if ($log->{state} eq 'msg_start' && /^$/) {
+			$log->{state} = 'msg';
+		} elsif ($log->{state} eq 'msg') {
+			if ($ret->{lines}) {
+				$ret->{msg} .= $_."\n";
+				unless (--$ret->{lines}) {
+					$log->{state} = 'sep';
 				}
 			} else {
 				croak "Log parse error at: $_\n",
-					$svn_log[$#svn_log]->{revision},"\n";
+					$ret->{revision},"\n";
 			}
 		}
 	}
-	close $log_fh or croak $?;
-	return \@svn_log;
+	return $ret;
 }
 
 sub svn_info {
@@ -1114,9 +1127,13 @@ __END__
 
 Data structures:
 
-@svn_log = array of log_msg hashes
+$svn_log hashref (as returned by svn_log_raw)
+{
+	fh => file handle of the log file,
+	state => state of the log file parser (sep/msg/rev/msg_start...)
+}
 
-$log_msg hash
+$log_msg hashref as returned by next_log_entry($svn_log)
 {
 	msg => 'whitespace-formatted log entry
 ',						# trailing newline is preserved
-- 
1.2.4.gb622a
Previous: Eric WongNext: James Cloos
Message 42 of 43 in “Errors GITtifying GCC and Binutils”
  1. Jan-Benedict GlawMar 22, 2006
  2. Linus TorvaldsMar 22, 2006
  3. Linus TorvaldsMar 23, 2006
  4. Linus TorvaldsMar 23, 2006
  5. Jan-Benedict GlawMar 23, 2006
  6. Linus TorvaldsMar 23, 2006
  7. Chris ShoemakerMar 24, 2006
  8. Keith PackardMar 24, 2006
  9. Jan-Benedict GlawMar 24, 2006
  10. Chris ShoemakerMar 25, 2006
  11. H. Peter AnvinMar 23, 2006
  12. Keith PackardMar 23, 2006
  13. Linus TorvaldsMar 23, 2006
  14. seanMar 23, 2006
  15. Linus TorvaldsMar 23, 2006
  16. Shawn PearceMar 23, 2006
  17. Ryan AndersonMar 23, 2006
  18. Junio C HamanoMar 24, 2006
  19. Junio C HamanoMar 23, 2006
  20. Johannes SchindelinMar 24, 2006
  21. Mark WoodingMar 24, 2006
  22. Andreas EricssonMar 24, 2006
  23. David S. MillerMar 23, 2006
  24. Linus TorvaldsMar 23, 2006
  25. Timo HirvonenMar 23, 2006
  26. seanMar 23, 2006
  27. Ralf BaechleMar 24, 2006
  28. Andreas EricssonMar 24, 2006
  29. Carl WorthMar 24, 2006
  30. Andreas EricssonMar 24, 2006
  31. Ryan AndersonMar 23, 2006
  32. Linus TorvaldsMar 23, 2006
  33. Junio C HamanoMar 23, 2006
  34. Ryan AndersonMar 24, 2006
  35. Junio C HamanoMar 24, 2006
  36. Ralf BaechleMar 24, 2006
  37. Jan-Benedict GlawMar 24, 2006
  38. Andreas EricssonMar 24, 2006
  39. Jan-Benedict GlawMar 25, 2006
  40. Santi BéjarMar 24, 2006
  41. Eric WongMar 25, 2006
  42. contrib/git-svn: stabilize memory usage for big fetchesEric Wong, Mar 26, 2006
  43. James CloosMar 25, 2006

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.