Git development
 help / color / mirror / Atom feed
* [PATCH 0/3] A smoother transition plan for cvsimport
From: Junio C Hamano @ 2013-01-14  1:40 UTC (permalink / raw)
  To: git; +Cc: jrnieder, mhagger, esr, chris
In-Reply-To: <7v8v7wiv3a.fsf@alter.siamese.dyndns.org>

So here is a start of how such a transition plan outlined in the
previous message may look like.

The first two are preparatory step to allow the current code to
still work even when cvsps2 and cvsps3 are both available on the
system.

The last patch is mostly for illustration purposes; the cvsimport-3.py
script it adds was taken from the patch Eric sent earlier and I relayed
to the list, and does not have later improvements in Eric's tree or
any of Chris's patches.

Junio C Hamano (3):
  cvsimport: allow setting a custom cvsps (2.x) program name
  cvsimport: introduce a version-switch wrapper
  cvsimport: start adding cvsps 3.x support

 .gitignore           |    1 +
 Makefile             |   28 +-
 git-cvsimport-2.perl | 1179 ++++++++++++++++++++++++++++++++++++++++++++++++++
 git-cvsimport-3.py   |  344 +++++++++++++++
 git-cvsimport.perl   | 1177 -------------------------------------------------
 git-cvsimport.sh     |    5 +
 t/lib-cvs.sh         |    4 +-
 7 files changed, 1553 insertions(+), 1185 deletions(-)
 create mode 100755 git-cvsimport-2.perl
 create mode 100755 git-cvsimport-3.py
 delete mode 100755 git-cvsimport.perl
 create mode 100755 git-cvsimport.sh

-- 
1.8.1.421.g6236851

^ permalink raw reply

* [PATCH 1/3] cvsimport: allow setting a custom cvsps (2.x) program name
From: Junio C Hamano @ 2013-01-14  1:40 UTC (permalink / raw)
  To: git; +Cc: jrnieder, mhagger, esr, chris
In-Reply-To: <1358127629-7500-1-git-send-email-gitster@pobox.com>

Distros may ship old cvsps under a different name, or the user may
install it outside the normal $PATH.  Allow setting CVSPS2_PATH from
the build environment.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Makefile           | 9 +++++++--
 git-cvsimport.perl | 4 +++-
 t/lib-cvs.sh       | 4 +++-
 3 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/Makefile b/Makefile
index 1b30d7b..8cb4a1b 100644
--- a/Makefile
+++ b/Makefile
@@ -571,9 +571,11 @@ endif
 ifndef PYTHON_PATH
 	PYTHON_PATH = /usr/bin/python
 endif
+ifndef CVSPS2_PATH
+	CVSPS2_PATH = cvsps
+endif
 
-export PERL_PATH
-export PYTHON_PATH
+export PERL_PATH PYTHON_PATH CVSPS2_PATH
 
 LIB_FILE = libgit.a
 XDIFF_LIB = xdiff/lib.a
@@ -1511,6 +1513,7 @@ SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
 TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
+CVSPS2_PATH_SQ = $(subst ','\'',$(CVSPS2_PATH))
 DIFF_SQ = $(subst ','\'',$(DIFF))
 
 LIBS = $(GITLIBS) $(EXTLIBS)
@@ -1724,6 +1727,7 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl GIT-VERSION-FILE
 	    -e '	H' \
 	    -e '	x' \
 	    -e '}' \
+	    -e 's|@@CVSPS2_PATH@@|$(CVSPS2_PATH_SQ)|g' \
 	    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
 	    $@.perl >$@+ && \
 	chmod +x $@+ && \
@@ -2102,6 +2106,7 @@ GIT-LDFLAGS: FORCE
 GIT-BUILD-OPTIONS: FORCE
 	@echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
 	@echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@
+	@echo CVSPS2_PATH=\''$(subst ','\'',$(CVSPS2_PATH_SQ))'\' >>$@
 	@echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@
 	@echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@
 	@echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 0a31ebd..ad460a5 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -33,6 +33,8 @@
 our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r, $opt_R);
 my (%conv_author_name, %conv_author_email, %conv_author_tz);
 
+my $cvsps2 = "@@CVSPS2_PATH@@";
+
 sub usage(;$) {
 	my $msg = shift;
 	print(STDERR "Error: $msg\n") if $msg;
@@ -751,7 +753,7 @@ sub munge_user_filename {
 		unless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {
 			push @opt, '--cvs-direct';
 		}
-		exec("cvsps","--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
+		exec($cvsps2,"--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
 		die "Could not start cvsps: $!\n";
 	}
 	($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh
index 44263ad..bdab63c 100644
--- a/t/lib-cvs.sh
+++ b/t/lib-cvs.sh
@@ -13,7 +13,9 @@ fi
 CVS="cvs -f"
 export CVS
 
-cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`
+CVSPS="$CVSPS2_PATH"
+
+cvsps_version=`$CVSPS -h 2>&1 | sed -ne 's/cvsps version //p'`
 case "$cvsps_version" in
 2.1 | 2.2*)
 	;;
-- 
1.8.1.421.g6236851

^ permalink raw reply related

* [PATCH 2/3] cvsimport: introduce a version-switch wrapper
From: Junio C Hamano @ 2013-01-14  1:40 UTC (permalink / raw)
  To: git; +Cc: jrnieder, mhagger, esr, chris
In-Reply-To: <1358127629-7500-1-git-send-email-gitster@pobox.com>

In preparation to have both old and new cvsimport during the
transition period, rename the cvsimport script to cvsimport-2
and introduce a small wrapper that we can later change it to
allow users to run either frontend (with their corresponding
cvsps backends).

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 .gitignore           |    1 +
 Makefile             |    3 +-
 git-cvsimport-2.perl | 1179 ++++++++++++++++++++++++++++++++++++++++++++++++++
 git-cvsimport.perl   | 1179 --------------------------------------------------
 git-cvsimport.sh     |    5 +
 5 files changed, 1187 insertions(+), 1180 deletions(-)
 create mode 100755 git-cvsimport-2.perl
 delete mode 100755 git-cvsimport.perl
 create mode 100755 git-cvsimport.sh

diff --git a/.gitignore b/.gitignore
index aa258a6..8cb799c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,6 +40,7 @@
 /git-credential-store
 /git-cvsexportcommit
 /git-cvsimport
+/git-cvsimport-2
 /git-cvsserver
 /git-daemon
 /git-diff
diff --git a/Makefile b/Makefile
index 8cb4a1b..b022db2 100644
--- a/Makefile
+++ b/Makefile
@@ -435,6 +435,7 @@ unexport CDPATH
 
 SCRIPT_SH += git-am.sh
 SCRIPT_SH += git-bisect.sh
+SCRIPT_SH += git-cvsimport.sh
 SCRIPT_SH += git-difftool--helper.sh
 SCRIPT_SH += git-filter-branch.sh
 SCRIPT_SH += git-lost-found.sh
@@ -463,7 +464,7 @@ SCRIPT_PERL += git-add--interactive.perl
 SCRIPT_PERL += git-difftool.perl
 SCRIPT_PERL += git-archimport.perl
 SCRIPT_PERL += git-cvsexportcommit.perl
-SCRIPT_PERL += git-cvsimport.perl
+SCRIPT_PERL += git-cvsimport-2.perl
 SCRIPT_PERL += git-cvsserver.perl
 SCRIPT_PERL += git-relink.perl
 SCRIPT_PERL += git-send-email.perl
diff --git a/git-cvsimport-2.perl b/git-cvsimport-2.perl
new file mode 100755
index 0000000..ad460a5
--- /dev/null
+++ b/git-cvsimport-2.perl
@@ -0,0 +1,1179 @@
+#!/usr/bin/perl
+
+# This tool is copyright (c) 2005, Matthias Urlichs.
+# It is released under the Gnu Public License, version 2.
+#
+# The basic idea is to aggregate CVS check-ins into related changes.
+# Fortunately, "cvsps" does that for us; all we have to do is to parse
+# its output.
+#
+# Checking out the files is done by a single long-running CVS connection
+# / server process.
+#
+# The head revision is on branch "origin" by default.
+# You can change that with the '-o' option.
+
+use 5.008;
+use strict;
+use warnings;
+use Getopt::Long;
+use File::Spec;
+use File::Temp qw(tempfile tmpnam);
+use File::Path qw(mkpath);
+use File::Basename qw(basename dirname);
+use Time::Local;
+use IO::Socket;
+use IO::Pipe;
+use POSIX qw(strftime tzset dup2 ENOENT);
+use IPC::Open2;
+
+$SIG{'PIPE'}="IGNORE";
+set_timezone('UTC');
+
+our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r, $opt_R);
+my (%conv_author_name, %conv_author_email, %conv_author_tz);
+
+my $cvsps2 = "@@CVSPS2_PATH@@";
+
+sub usage(;$) {
+	my $msg = shift;
+	print(STDERR "Error: $msg\n") if $msg;
+	print STDERR <<END;
+Usage: git cvsimport     # fetch/update GIT from CVS
+       [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
+       [-p opts-for-cvsps] [-P file] [-C GIT_repository] [-z fuzz] [-i] [-k]
+       [-u] [-s subst] [-a] [-m] [-M regex] [-S regex] [-L commitlimit]
+       [-r remote] [-R] [CVS_module]
+END
+	exit(1);
+}
+
+sub read_author_info($) {
+	my ($file) = @_;
+	my $user;
+	open my $f, '<', "$file" or die("Failed to open $file: $!\n");
+
+	while (<$f>) {
+		# Expected format is this:
+		#   exon=Andreas Ericsson <ae@op5.se>
+		if (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/) {
+			$user = $1;
+			$conv_author_name{$user} = $2;
+			$conv_author_email{$user} = $3;
+		}
+		# or with an optional timezone:
+		#   spawn=Simon Pawn <spawn@frog-pond.org> America/Chicago
+		elsif (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*(\S+?)\s*$/) {
+			$user = $1;
+			$conv_author_name{$user} = $2;
+			$conv_author_email{$user} = $3;
+			$conv_author_tz{$user} = $4;
+		}
+		# However, we also read from CVSROOT/users format
+		# to ease migration.
+		elsif (/^(\w+):(['"]?)(.+?)\2\s*$/) {
+			my $mapped;
+			($user, $mapped) = ($1, $3);
+			if ($mapped =~ /^\s*(.*?)\s*<(.*)>\s*$/) {
+				$conv_author_name{$user} = $1;
+				$conv_author_email{$user} = $2;
+			}
+			elsif ($mapped =~ /^<?(.*)>?$/) {
+				$conv_author_name{$user} = $user;
+				$conv_author_email{$user} = $1;
+			}
+		}
+		# NEEDSWORK: Maybe warn on unrecognized lines?
+	}
+	close ($f);
+}
+
+sub write_author_info($) {
+	my ($file) = @_;
+	open my $f, '>', $file or
+	  die("Failed to open $file for writing: $!");
+
+	foreach (keys %conv_author_name) {
+		print $f "$_=$conv_author_name{$_} <$conv_author_email{$_}>";
+		print $f " $conv_author_tz{$_}" if ($conv_author_tz{$_});
+		print $f "\n";
+	}
+	close ($f);
+}
+
+# Versions of perl before 5.10.0 may not automatically check $TZ each
+# time localtime is run (most platforms will do so only the first time).
+# We can work around this by using tzset() to update the internal
+# variable whenever we change the environment.
+sub set_timezone {
+	$ENV{TZ} = shift;
+	tzset();
+}
+
+# convert getopts specs for use by git config
+my %longmap = (
+	'A:' => 'authors-file',
+	'M:' => 'merge-regex',
+	'P:' => undef,
+	'R' => 'track-revisions',
+	'S:' => 'ignore-paths',
+);
+
+sub read_repo_config {
+	# Split the string between characters, unless there is a ':'
+	# So "abc:de" becomes ["a", "b", "c:", "d", "e"]
+	my @opts = split(/ *(?!:)/, shift);
+	foreach my $o (@opts) {
+		my $key = $o;
+		$key =~ s/://g;
+		my $arg = 'git config';
+		$arg .= ' --bool' if ($o !~ /:$/);
+		my $ckey = $key;
+
+		if (exists $longmap{$o}) {
+			# An uppercase option like -R cannot be
+			# expressed in the configuration, as the
+			# variable names are downcased.
+			$ckey = $longmap{$o};
+			next if (! defined $ckey);
+			$ckey =~ s/-//g;
+		}
+		chomp(my $tmp = `$arg --get cvsimport.$ckey`);
+		if ($tmp && !($arg =~ /--bool/ && $tmp eq 'false')) {
+			no strict 'refs';
+			my $opt_name = "opt_" . $key;
+			if (!$$opt_name) {
+				$$opt_name = $tmp;
+			}
+		}
+	}
+}
+
+my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:R";
+read_repo_config($opts);
+Getopt::Long::Configure( 'no_ignore_case', 'bundling' );
+
+# turn the Getopt::Std specification in a Getopt::Long one,
+# with support for multiple -M options
+GetOptions( map { s/:/=s/; /M/ ? "$_\@" : $_ } split( /(?!:)/, $opts ) )
+    or usage();
+usage if $opt_h;
+
+if (@ARGV == 0) {
+		chomp(my $module = `git config --get cvsimport.module`);
+		push(@ARGV, $module) if $? == 0;
+}
+@ARGV <= 1 or usage("You can't specify more than one CVS module");
+
+if ($opt_d) {
+	$ENV{"CVSROOT"} = $opt_d;
+} elsif (-f 'CVS/Root') {
+	open my $f, '<', 'CVS/Root' or die 'Failed to open CVS/Root';
+	$opt_d = <$f>;
+	chomp $opt_d;
+	close $f;
+	$ENV{"CVSROOT"} = $opt_d;
+} elsif ($ENV{"CVSROOT"}) {
+	$opt_d = $ENV{"CVSROOT"};
+} else {
+	usage("CVSROOT needs to be set");
+}
+$opt_s ||= "-";
+$opt_a ||= 0;
+
+my $git_tree = $opt_C;
+$git_tree ||= ".";
+
+my $remote;
+if (defined $opt_r) {
+	$remote = 'refs/remotes/' . $opt_r;
+	$opt_o ||= "master";
+} else {
+	$opt_o ||= "origin";
+	$remote = 'refs/heads';
+}
+
+my $cvs_tree;
+if ($#ARGV == 0) {
+	$cvs_tree = $ARGV[0];
+} elsif (-f 'CVS/Repository') {
+	open my $f, '<', 'CVS/Repository' or
+	    die 'Failed to open CVS/Repository';
+	$cvs_tree = <$f>;
+	chomp $cvs_tree;
+	close $f;
+} else {
+	usage("CVS module has to be specified");
+}
+
+our @mergerx = ();
+if ($opt_m) {
+	@mergerx = ( qr/\b(?:from|of|merge|merging|merged) ([-\w]+)/i );
+}
+if (@opt_M) {
+	push (@mergerx, map { qr/$_/ } @opt_M);
+}
+
+# Remember UTC of our starting time
+# we'll want to avoid importing commits
+# that are too recent
+our $starttime = time();
+
+select(STDERR); $|=1; select(STDOUT);
+
+
+package CVSconn;
+# Basic CVS dialog.
+# We're only interested in connecting and downloading, so ...
+
+use File::Spec;
+use File::Temp qw(tempfile);
+use POSIX qw(strftime dup2);
+
+sub new {
+	my ($what,$repo,$subdir) = @_;
+	$what=ref($what) if ref($what);
+
+	my $self = {};
+	$self->{'buffer'} = "";
+	bless($self,$what);
+
+	$repo =~ s#/+$##;
+	$self->{'fullrep'} = $repo;
+	$self->conn();
+
+	$self->{'subdir'} = $subdir;
+	$self->{'lines'} = undef;
+
+	return $self;
+}
+
+sub find_password_entry {
+	my ($cvspass, @cvsroot) = @_;
+	my ($file, $delim) = @$cvspass;
+	my $pass;
+	local ($_);
+
+	if (open(my $fh, $file)) {
+		# :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
+		CVSPASSFILE:
+		while (<$fh>) {
+			chomp;
+			s/^\/\d+\s+//;
+			my ($w, $p) = split($delim,$_,2);
+			for my $cvsroot (@cvsroot) {
+				if ($w eq $cvsroot) {
+					$pass = $p;
+					last CVSPASSFILE;
+				}
+			}
+		}
+		close($fh);
+	}
+	return $pass;
+}
+
+sub conn {
+	my $self = shift;
+	my $repo = $self->{'fullrep'};
+	if ($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) {
+		my ($param,$user,$pass,$serv,$port) = ($1,$2,$3,$4,$5);
+
+		my ($proxyhost,$proxyport);
+		if ($param && ($param =~ m/proxy=([^;]+)/)) {
+			$proxyhost = $1;
+			# Default proxyport, if not specified, is 8080.
+			$proxyport = 8080;
+			if ($ENV{"CVS_PROXY_PORT"}) {
+				$proxyport = $ENV{"CVS_PROXY_PORT"};
+			}
+			if ($param =~ m/proxyport=([^;]+)/) {
+				$proxyport = $1;
+			}
+		}
+		$repo ||= '/';
+
+		# if username is not explicit in CVSROOT, then use current user, as cvs would
+		$user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user;
+		my $rr2 = "-";
+		unless ($port) {
+			$rr2 = ":pserver:$user\@$serv:$repo";
+			$port=2401;
+		}
+		my $rr = ":pserver:$user\@$serv:$port$repo";
+
+		if ($pass) {
+			$pass = $self->_scramble($pass);
+		} else {
+			my @cvspass = ([$ENV{'HOME'}."/.cvspass", qr/\s/],
+				       [$ENV{'HOME'}."/.cvs/cvspass", qr/=/]);
+			my @loc = ();
+			foreach my $cvspass (@cvspass) {
+				my $p = find_password_entry($cvspass, $rr, $rr2);
+				if ($p) {
+					push @loc, $cvspass->[0];
+					$pass = $p;
+				}
+			}
+
+			if (1 < @loc) {
+				die("Multiple cvs password files have ".
+				    "entries for CVSROOT $opt_d: @loc");
+			} elsif (!$pass) {
+				$pass = "A";
+			}
+		}
+
+		my ($s, $rep);
+		if ($proxyhost) {
+
+			# Use a HTTP Proxy. Only works for HTTP proxies that
+			# don't require user authentication
+			#
+			# See: http://www.ietf.org/rfc/rfc2817.txt
+
+			$s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport);
+			die "Socket to $proxyhost: $!\n" unless defined $s;
+			$s->write("CONNECT $serv:$port HTTP/1.1\r\nHost: $serv:$port\r\n\r\n")
+	                        or die "Write to $proxyhost: $!\n";
+	                $s->flush();
+
+			$rep = <$s>;
+
+			# The answer should look like 'HTTP/1.x 2yy ....'
+			if (!($rep =~ m#^HTTP/1\.. 2[0-9][0-9]#)) {
+				die "Proxy connect: $rep\n";
+			}
+			# Skip up to the empty line of the proxy server output
+			# including the response headers.
+			while ($rep = <$s>) {
+				last if (!defined $rep ||
+					 $rep eq "\n" ||
+					 $rep eq "\r\n");
+			}
+		} else {
+			$s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port);
+			die "Socket to $serv: $!\n" unless defined $s;
+		}
+
+		$s->write("BEGIN AUTH REQUEST\n$repo\n$user\n$pass\nEND AUTH REQUEST\n")
+			or die "Write to $serv: $!\n";
+		$s->flush();
+
+		$rep = <$s>;
+
+		if ($rep ne "I LOVE YOU\n") {
+			$rep="<unknown>" unless $rep;
+			die "AuthReply: $rep\n";
+		}
+		$self->{'socketo'} = $s;
+		$self->{'socketi'} = $s;
+	} else { # local or ext: Fork off our own cvs server.
+		my $pr = IO::Pipe->new();
+		my $pw = IO::Pipe->new();
+		my $pid = fork();
+		die "Fork: $!\n" unless defined $pid;
+		my $cvs = 'cvs';
+		$cvs = $ENV{CVS_SERVER} if exists $ENV{CVS_SERVER};
+		my $rsh = 'rsh';
+		$rsh = $ENV{CVS_RSH} if exists $ENV{CVS_RSH};
+
+		my @cvs = ($cvs, 'server');
+		my ($local, $user, $host);
+		$local = $repo =~ s/:local://;
+		if (!$local) {
+		    $repo =~ s/:ext://;
+		    $local = !($repo =~ s/^(?:([^\@:]+)\@)?([^:]+)://);
+		    ($user, $host) = ($1, $2);
+		}
+		if (!$local) {
+		    if ($user) {
+			unshift @cvs, $rsh, '-l', $user, $host;
+		    } else {
+			unshift @cvs, $rsh, $host;
+		    }
+		}
+
+		unless ($pid) {
+			$pr->writer();
+			$pw->reader();
+			dup2($pw->fileno(),0);
+			dup2($pr->fileno(),1);
+			$pr->close();
+			$pw->close();
+			exec(@cvs);
+		}
+		$pw->writer();
+		$pr->reader();
+		$self->{'socketo'} = $pw;
+		$self->{'socketi'} = $pr;
+	}
+	$self->{'socketo'}->write("Root $repo\n");
+
+	# Trial and error says that this probably is the minimum set
+	$self->{'socketo'}->write("Valid-responses ok error Valid-requests Mode M Mbinary E Checked-in Created Updated Merged Removed\n");
+
+	$self->{'socketo'}->write("valid-requests\n");
+	$self->{'socketo'}->flush();
+
+	my $rep=$self->readline();
+	die "Failed to read from server" unless defined $rep;
+	chomp($rep);
+	if ($rep !~ s/^Valid-requests\s*//) {
+		$rep="<unknown>" unless $rep;
+		die "Expected Valid-requests from server, but got: $rep\n";
+	}
+	chomp(my $res=$self->readline());
+	die "validReply: $res\n" if $res ne "ok";
+
+	$self->{'socketo'}->write("UseUnchanged\n") if $rep =~ /\bUseUnchanged\b/;
+	$self->{'repo'} = $repo;
+}
+
+sub readline {
+	my ($self) = @_;
+	return $self->{'socketi'}->getline();
+}
+
+sub _file {
+	# Request a file with a given revision.
+	# Trial and error says this is a good way to do it. :-/
+	my ($self,$fn,$rev) = @_;
+	$self->{'socketo'}->write("Argument -N\n") or return undef;
+	$self->{'socketo'}->write("Argument -P\n") or return undef;
+	# -kk: Linus' version doesn't use it - defaults to off
+	if ($opt_k) {
+	    $self->{'socketo'}->write("Argument -kk\n") or return undef;
+	}
+	$self->{'socketo'}->write("Argument -r\n") or return undef;
+	$self->{'socketo'}->write("Argument $rev\n") or return undef;
+	$self->{'socketo'}->write("Argument --\n") or return undef;
+	$self->{'socketo'}->write("Argument $self->{'subdir'}/$fn\n") or return undef;
+	$self->{'socketo'}->write("Directory .\n") or return undef;
+	$self->{'socketo'}->write("$self->{'repo'}\n") or return undef;
+	# $self->{'socketo'}->write("Sticky T1.0\n") or return undef;
+	$self->{'socketo'}->write("co\n") or return undef;
+	$self->{'socketo'}->flush() or return undef;
+	$self->{'lines'} = 0;
+	return 1;
+}
+sub _line {
+	# Read a line from the server.
+	# ... except that 'line' may be an entire file. ;-)
+	my ($self, $fh) = @_;
+	die "Not in lines" unless defined $self->{'lines'};
+
+	my $line;
+	my $res=0;
+	while (defined($line = $self->readline())) {
+		# M U gnupg-cvs-rep/AUTHORS
+		# Updated gnupg-cvs-rep/
+		# /daten/src/rsync/gnupg-cvs-rep/AUTHORS
+		# /AUTHORS/1.1///T1.1
+		# u=rw,g=rw,o=rw
+		# 0
+		# ok
+
+		if ($line =~ s/^(?:Created|Updated) //) {
+			$line = $self->readline(); # path
+			$line = $self->readline(); # Entries line
+			my $mode = $self->readline(); chomp $mode;
+			$self->{'mode'} = $mode;
+			defined (my $cnt = $self->readline())
+				or die "EOF from server after 'Changed'\n";
+			chomp $cnt;
+			die "Duh: Filesize $cnt" if $cnt !~ /^\d+$/;
+			$line="";
+			$res = $self->_fetchfile($fh, $cnt);
+		} elsif ($line =~ s/^ //) {
+			print $fh $line;
+			$res += length($line);
+		} elsif ($line =~ /^M\b/) {
+			# output, do nothing
+		} elsif ($line =~ /^Mbinary\b/) {
+			my $cnt;
+			die "EOF from server after 'Mbinary'" unless defined ($cnt = $self->readline());
+			chomp $cnt;
+			die "Duh: Mbinary $cnt" if $cnt !~ /^\d+$/ or $cnt<1;
+			$line="";
+			$res += $self->_fetchfile($fh, $cnt);
+		} else {
+			chomp $line;
+			if ($line eq "ok") {
+				# print STDERR "S: ok (".length($res).")\n";
+				return $res;
+			} elsif ($line =~ s/^E //) {
+				# print STDERR "S: $line\n";
+			} elsif ($line =~ /^(Remove-entry|Removed) /i) {
+				$line = $self->readline(); # filename
+				$line = $self->readline(); # OK
+				chomp $line;
+				die "Unknown: $line" if $line ne "ok";
+				return -1;
+			} else {
+				die "Unknown: $line\n";
+			}
+		}
+	}
+	return undef;
+}
+sub file {
+	my ($self,$fn,$rev) = @_;
+	my $res;
+
+	my ($fh, $name) = tempfile('gitcvs.XXXXXX',
+		    DIR => File::Spec->tmpdir(), UNLINK => 1);
+
+	$self->_file($fn,$rev) and $res = $self->_line($fh);
+
+	if (!defined $res) {
+	    print STDERR "Server has gone away while fetching $fn $rev, retrying...\n";
+	    truncate $fh, 0;
+	    $self->conn();
+	    $self->_file($fn,$rev) or die "No file command send";
+	    $res = $self->_line($fh);
+	    die "Retry failed" unless defined $res;
+	}
+	close ($fh);
+
+	return ($name, $res);
+}
+sub _fetchfile {
+	my ($self, $fh, $cnt) = @_;
+	my $res = 0;
+	my $bufsize = 1024 * 1024;
+	while ($cnt) {
+	    if ($bufsize > $cnt) {
+		$bufsize = $cnt;
+	    }
+	    my $buf;
+	    my $num = $self->{'socketi'}->read($buf,$bufsize);
+	    die "Server: Filesize $cnt: $num: $!\n" if not defined $num or $num<=0;
+	    print $fh $buf;
+	    $res += $num;
+	    $cnt -= $num;
+	}
+	return $res;
+}
+
+sub _scramble {
+	my ($self, $pass) = @_;
+	my $scrambled = "A";
+
+	return $scrambled unless $pass;
+
+	my $pass_len = length($pass);
+	my @pass_arr = split("", $pass);
+	my $i;
+
+	# from cvs/src/scramble.c
+	my @shifts = (
+		  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+		 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+		114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
+		111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
+		 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
+		125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
+		 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
+		 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
+		225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
+		199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
+		174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
+		207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
+		192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
+		227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
+		182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
+		243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
+	);
+
+	for ($i = 0; $i < $pass_len; $i++) {
+		$scrambled .= pack("C", $shifts[ord($pass_arr[$i])]);
+	}
+
+	return $scrambled;
+}
+
+package main;
+
+my $cvs = CVSconn->new($opt_d, $cvs_tree);
+
+
+sub pdate($) {
+	my ($d) = @_;
+	m#(\d{2,4})/(\d\d)/(\d\d)\s(\d\d):(\d\d)(?::(\d\d))?#
+		or die "Unparseable date: $d\n";
+	my $y=$1; $y-=1900 if $y>1900;
+	return timegm($6||0,$5,$4,$3,$2-1,$y);
+}
+
+sub pmode($) {
+	my ($mode) = @_;
+	my $m = 0;
+	my $mm = 0;
+	my $um = 0;
+	for my $x(split(//,$mode)) {
+		if ($x eq ",") {
+			$m |= $mm&$um;
+			$mm = 0;
+			$um = 0;
+		} elsif ($x eq "u") { $um |= 0700;
+		} elsif ($x eq "g") { $um |= 0070;
+		} elsif ($x eq "o") { $um |= 0007;
+		} elsif ($x eq "r") { $mm |= 0444;
+		} elsif ($x eq "w") { $mm |= 0222;
+		} elsif ($x eq "x") { $mm |= 0111;
+		} elsif ($x eq "=") { # do nothing
+		} else { die "Unknown mode: $mode\n";
+		}
+	}
+	$m |= $mm&$um;
+	return $m;
+}
+
+sub getwd() {
+	my $pwd = `pwd`;
+	chomp $pwd;
+	return $pwd;
+}
+
+sub is_sha1 {
+	my $s = shift;
+	return $s =~ /^[a-f0-9]{40}$/;
+}
+
+sub get_headref ($) {
+	my $name = shift;
+	my $r = `git rev-parse --verify '$name' 2>/dev/null`;
+	return undef unless $? == 0;
+	chomp $r;
+	return $r;
+}
+
+my $user_filename_prepend = '';
+sub munge_user_filename {
+	my $name = shift;
+	return File::Spec->file_name_is_absolute($name) ?
+		$name :
+		$user_filename_prepend . $name;
+}
+
+-d $git_tree
+	or mkdir($git_tree,0777)
+	or die "Could not create $git_tree: $!";
+if ($git_tree ne '.') {
+	$user_filename_prepend = getwd() . '/';
+	chdir($git_tree);
+}
+
+my $last_branch = "";
+my $orig_branch = "";
+my %branch_date;
+my $tip_at_start = undef;
+
+my $git_dir = $ENV{"GIT_DIR"} || ".git";
+$git_dir = getwd()."/".$git_dir unless $git_dir =~ m#^/#;
+$ENV{"GIT_DIR"} = $git_dir;
+my $orig_git_index;
+$orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE};
+
+my %index; # holds filenames of one index per branch
+
+unless (-d $git_dir) {
+	system(qw(git init));
+	die "Cannot init the GIT db at $git_tree: $?\n" if $?;
+	system(qw(git read-tree --empty));
+	die "Cannot init an empty tree: $?\n" if $?;
+
+	$last_branch = $opt_o;
+	$orig_branch = "";
+} else {
+	open(F, "-|", qw(git symbolic-ref HEAD)) or
+		die "Cannot run git symbolic-ref: $!\n";
+	chomp ($last_branch = <F>);
+	$last_branch = basename($last_branch);
+	close(F);
+	unless ($last_branch) {
+		warn "Cannot read the last branch name: $! -- assuming 'master'\n";
+		$last_branch = "master";
+	}
+	$orig_branch = $last_branch;
+	$tip_at_start = `git rev-parse --verify HEAD`;
+
+	# Get the last import timestamps
+	my $fmt = '($ref, $author) = (%(refname), %(author));';
+	my @cmd = ('git', 'for-each-ref', '--perl', "--format=$fmt", $remote);
+	open(H, "-|", @cmd) or die "Cannot run git for-each-ref: $!\n";
+	while (defined(my $entry = <H>)) {
+		my ($ref, $author);
+		eval($entry) || die "cannot eval refs list: $@";
+		my ($head) = ($ref =~ m|^$remote/(.*)|);
+		$author =~ /^.*\s(\d+)\s[-+]\d{4}$/;
+		$branch_date{$head} = $1;
+	}
+	close(H);
+        if (!exists $branch_date{$opt_o}) {
+		die "Branch '$opt_o' does not exist.\n".
+		       "Either use the correct '-o branch' option,\n".
+		       "or import to a new repository.\n";
+        }
+}
+
+-d $git_dir
+	or die "Could not create git subdir ($git_dir).\n";
+
+# now we read (and possibly save) author-info as well
+-f "$git_dir/cvs-authors" and
+  read_author_info("$git_dir/cvs-authors");
+if ($opt_A) {
+	read_author_info(munge_user_filename($opt_A));
+	write_author_info("$git_dir/cvs-authors");
+}
+
+# open .git/cvs-revisions, if requested
+open my $revision_map, '>>', "$git_dir/cvs-revisions"
+    or die "Can't open $git_dir/cvs-revisions for appending: $!\n"
+	if defined $opt_R;
+
+
+#
+# run cvsps into a file unless we are getting
+# it passed as a file via $opt_P
+#
+my $cvspsfile;
+unless ($opt_P) {
+	print "Running cvsps...\n" if $opt_v;
+	my $pid = open(CVSPS,"-|");
+	my $cvspsfh;
+	die "Cannot fork: $!\n" unless defined $pid;
+	unless ($pid) {
+		my @opt;
+		@opt = split(/,/,$opt_p) if defined $opt_p;
+		unshift @opt, '-z', $opt_z if defined $opt_z;
+		unshift @opt, '-q'         unless defined $opt_v;
+		unless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {
+			push @opt, '--cvs-direct';
+		}
+		exec($cvsps2,"--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
+		die "Could not start cvsps: $!\n";
+	}
+	($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
+					  DIR => File::Spec->tmpdir());
+	while (<CVSPS>) {
+	    print $cvspsfh $_;
+	}
+	close CVSPS;
+	$? == 0 or die "git cvsimport: fatal: cvsps reported error\n";
+	close $cvspsfh;
+} else {
+	$cvspsfile = munge_user_filename($opt_P);
+}
+
+open(CVS, "<$cvspsfile") or die $!;
+
+## cvsps output:
+#---------------------
+#PatchSet 314
+#Date: 1999/09/18 13:03:59
+#Author: wkoch
+#Branch: STABLE-BRANCH-1-0
+#Ancestor branch: HEAD
+#Tag: (none)
+#Log:
+#    See ChangeLog: Sat Sep 18 13:03:28 CEST 1999  Werner Koch
+#Members:
+#	README:1.57->1.57.2.1
+#	VERSION:1.96->1.96.2.1
+#
+#---------------------
+
+my $state = 0;
+
+sub update_index (\@\@) {
+	my $old = shift;
+	my $new = shift;
+	open(my $fh, '|-', qw(git update-index -z --index-info))
+		or die "unable to open git update-index: $!";
+	print $fh
+		(map { "0 0000000000000000000000000000000000000000\t$_\0" }
+			@$old),
+		(map { '100' . sprintf('%o', $_->[0]) . " $_->[1]\t$_->[2]\0" }
+			@$new)
+		or die "unable to write to git update-index: $!";
+	close $fh
+		or die "unable to write to git update-index: $!";
+	$? and die "git update-index reported error: $?";
+}
+
+sub write_tree () {
+	open(my $fh, '-|', qw(git write-tree))
+		or die "unable to open git write-tree: $!";
+	chomp(my $tree = <$fh>);
+	is_sha1($tree)
+		or die "Cannot get tree id ($tree): $!";
+	close($fh)
+		or die "Error running git write-tree: $?\n";
+	print "Tree ID $tree\n" if $opt_v;
+	return $tree;
+}
+
+my ($patchset,$date,$author_name,$author_email,$author_tz,$branch,$ancestor,$tag,$logmsg);
+my (@old,@new,@skipped,%ignorebranch,@commit_revisions);
+
+# commits that cvsps cannot place anywhere...
+$ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
+
+sub commit {
+	if ($branch eq $opt_o && !$index{branch} &&
+		!get_headref("$remote/$branch")) {
+	    # looks like an initial commit
+	    # use the index primed by git init
+	    $ENV{GIT_INDEX_FILE} = "$git_dir/index";
+	    $index{$branch} = "$git_dir/index";
+	} else {
+	    # use an index per branch to speed up
+	    # imports of projects with many branches
+	    unless ($index{$branch}) {
+		$index{$branch} = tmpnam();
+		$ENV{GIT_INDEX_FILE} = $index{$branch};
+		if ($ancestor) {
+		    system("git", "read-tree", "$remote/$ancestor");
+		} else {
+		    system("git", "read-tree", "$remote/$branch");
+		}
+		die "read-tree failed: $?\n" if $?;
+	    }
+	}
+        $ENV{GIT_INDEX_FILE} = $index{$branch};
+
+	update_index(@old, @new);
+	@old = @new = ();
+	my $tree = write_tree();
+	my $parent = get_headref("$remote/$last_branch");
+	print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v;
+
+	my @commit_args;
+	push @commit_args, ("-p", $parent) if $parent;
+
+	# loose detection of merges
+	# based on the commit msg
+	foreach my $rx (@mergerx) {
+		next unless $logmsg =~ $rx && $1;
+		my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
+		if (my $sha1 = get_headref("$remote/$mparent")) {
+			push @commit_args, '-p', "$remote/$mparent";
+			print "Merge parent branch: $mparent\n" if $opt_v;
+		}
+	}
+
+	set_timezone($author_tz);
+	my $commit_date = strftime("%s %z", localtime($date));
+	set_timezone('UTC');
+	$ENV{GIT_AUTHOR_NAME} = $author_name;
+	$ENV{GIT_AUTHOR_EMAIL} = $author_email;
+	$ENV{GIT_AUTHOR_DATE} = $commit_date;
+	$ENV{GIT_COMMITTER_NAME} = $author_name;
+	$ENV{GIT_COMMITTER_EMAIL} = $author_email;
+	$ENV{GIT_COMMITTER_DATE} = $commit_date;
+	my $pid = open2(my $commit_read, my $commit_write,
+		'git', 'commit-tree', $tree, @commit_args);
+
+	# compatibility with git2cvs
+	substr($logmsg,32767) = "" if length($logmsg) > 32767;
+	$logmsg =~ s/[\s\n]+\z//;
+
+	if (@skipped) {
+	    $logmsg .= "\n\n\nSKIPPED:\n\t";
+	    $logmsg .= join("\n\t", @skipped) . "\n";
+	    @skipped = ();
+	}
+
+	print($commit_write "$logmsg\n") && close($commit_write)
+		or die "Error writing to git commit-tree: $!\n";
+
+	print "Committed patch $patchset ($branch $commit_date)\n" if $opt_v;
+	chomp(my $cid = <$commit_read>);
+	is_sha1($cid) or die "Cannot get commit id ($cid): $!\n";
+	print "Commit ID $cid\n" if $opt_v;
+	close($commit_read);
+
+	waitpid($pid,0);
+	die "Error running git commit-tree: $?\n" if $?;
+
+	system('git' , 'update-ref', "$remote/$branch", $cid) == 0
+		or die "Cannot write branch $branch for update: $!\n";
+
+	if ($revision_map) {
+		print $revision_map "@$_ $cid\n" for @commit_revisions;
+	}
+	@commit_revisions = ();
+
+	if ($tag) {
+	        my ($xtag) = $tag;
+		$xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **
+		$xtag =~ tr/_/\./ if ( $opt_u );
+		$xtag =~ s/[\/]/$opt_s/g;
+
+		# See refs.c for these rules.
+		# Tag cannot contain bad chars. (See bad_ref_char in refs.c.)
+		$xtag =~ s/[ ~\^:\\\*\?\[]//g;
+		# Other bad strings for tags:
+		# (See check_refname_component in refs.c.)
+		1 while $xtag =~ s/
+			(?: \.\.        # Tag cannot contain '..'.
+			|   \@{         # Tag cannot contain '@{'.
+			| ^ -           # Tag cannot begin with '-'.
+			|   \.lock $    # Tag cannot end with '.lock'.
+			| ^ \.          # Tag cannot begin...
+			|   \. $        # ...or end with '.'
+			)//xg;
+		# Tag cannot be empty.
+		if ($xtag eq '') {
+			warn("warning: ignoring tag '$tag'",
+			" with invalid tagname\n");
+			return;
+		}
+
+		if (system('git' , 'tag', '-f', $xtag, $cid) != 0) {
+			# We did our best to sanitize the tag, but still failed
+			# for whatever reason. Bail out, and give the user
+			# enough information to understand if/how we should
+			# improve the translation in the future.
+			if ($tag ne $xtag) {
+				print "Translated '$tag' tag to '$xtag'\n";
+			}
+			die "Cannot create tag $xtag: $!\n";
+		}
+
+		print "Created tag '$xtag' on '$branch'\n" if $opt_v;
+	}
+};
+
+my $commitcount = 1;
+while (<CVS>) {
+	chomp;
+	if ($state == 0 and /^-+$/) {
+		$state = 1;
+	} elsif ($state == 0) {
+		$state = 1;
+		redo;
+	} elsif (($state==0 or $state==1) and s/^PatchSet\s+//) {
+		$patchset = 0+$_;
+		$state=2;
+	} elsif ($state == 2 and s/^Date:\s+//) {
+		$date = pdate($_);
+		unless ($date) {
+			print STDERR "Could not parse date: $_\n";
+			$state=0;
+			next;
+		}
+		$state=3;
+	} elsif ($state == 3 and s/^Author:\s+//) {
+		$author_tz = "UTC";
+		s/\s+$//;
+		if (/^(.*?)\s+<(.*)>/) {
+		    ($author_name, $author_email) = ($1, $2);
+		} elsif ($conv_author_name{$_}) {
+			$author_name = $conv_author_name{$_};
+			$author_email = $conv_author_email{$_};
+			$author_tz = $conv_author_tz{$_} if ($conv_author_tz{$_});
+		} else {
+		    $author_name = $author_email = $_;
+		}
+		$state = 4;
+	} elsif ($state == 4 and s/^Branch:\s+//) {
+		s/\s+$//;
+		tr/_/\./ if ( $opt_u );
+		s/[\/]/$opt_s/g;
+		$branch = $_;
+		$state = 5;
+	} elsif ($state == 5 and s/^Ancestor branch:\s+//) {
+		s/\s+$//;
+		$ancestor = $_;
+		$ancestor = $opt_o if $ancestor eq "HEAD";
+		$state = 6;
+	} elsif ($state == 5) {
+		$ancestor = undef;
+		$state = 6;
+		redo;
+	} elsif ($state == 6 and s/^Tag:\s+//) {
+		s/\s+$//;
+		if ($_ eq "(none)") {
+			$tag = undef;
+		} else {
+			$tag = $_;
+		}
+		$state = 7;
+	} elsif ($state == 7 and /^Log:/) {
+		$logmsg = "";
+		$state = 8;
+	} elsif ($state == 8 and /^Members:/) {
+		$branch = $opt_o if $branch eq "HEAD";
+		if (defined $branch_date{$branch} and $branch_date{$branch} >= $date) {
+			# skip
+			print "skip patchset $patchset: $date before $branch_date{$branch}\n" if $opt_v;
+			$state = 11;
+			next;
+		}
+		if (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {
+			# skip if the commit is too recent
+			# given that the cvsps default fuzz is 300s, we give ourselves another
+			# 300s just in case -- this also prevents skipping commits
+			# due to server clock drift
+			print "skip patchset $patchset: $date too recent\n" if $opt_v;
+			$state = 11;
+			next;
+		}
+		if (exists $ignorebranch{$branch}) {
+			print STDERR "Skipping $branch\n";
+			$state = 11;
+			next;
+		}
+		if ($ancestor) {
+			if ($ancestor eq $branch) {
+				print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n";
+				$ancestor = $opt_o;
+			}
+			if (defined get_headref("$remote/$branch")) {
+				print STDERR "Branch $branch already exists!\n";
+				$state=11;
+				next;
+			}
+			my $id = get_headref("$remote/$ancestor");
+			if (!$id) {
+				print STDERR "Branch $ancestor does not exist!\n";
+				$ignorebranch{$branch} = 1;
+				$state=11;
+				next;
+			}
+
+			system(qw(git update-ref -m cvsimport),
+				"$remote/$branch", $id);
+			if($? != 0) {
+				print STDERR "Could not create branch $branch\n";
+				$ignorebranch{$branch} = 1;
+				$state=11;
+				next;
+			}
+		}
+		$last_branch = $branch if $branch ne $last_branch;
+		$state = 9;
+	} elsif ($state == 8) {
+		$logmsg .= "$_\n";
+	} elsif ($state == 9 and /^\s+(.+?):(INITIAL|\d+(?:\.\d+)+)->(\d+(?:\.\d+)+)\s*$/) {
+#	VERSION:1.96->1.96.2.1
+		my $init = ($2 eq "INITIAL");
+		my $fn = $1;
+		my $rev = $3;
+		$fn =~ s#^/+##;
+		if ($opt_S && $fn =~ m/$opt_S/) {
+		    print "SKIPPING $fn v $rev\n";
+		    push(@skipped, $fn);
+		    next;
+		}
+		push @commit_revisions, [$fn, $rev];
+		print "Fetching $fn   v $rev\n" if $opt_v;
+		my ($tmpname, $size) = $cvs->file($fn,$rev);
+		if ($size == -1) {
+			push(@old,$fn);
+			print "Drop $fn\n" if $opt_v;
+		} else {
+			print "".($init ? "New" : "Update")." $fn: $size bytes\n" if $opt_v;
+			my $pid = open(my $F, '-|');
+			die $! unless defined $pid;
+			if (!$pid) {
+			    exec("git", "hash-object", "-w", $tmpname)
+				or die "Cannot create object: $!\n";
+			}
+			my $sha = <$F>;
+			chomp $sha;
+			close $F;
+			my $mode = pmode($cvs->{'mode'});
+			push(@new,[$mode, $sha, $fn]); # may be resurrected!
+		}
+		unlink($tmpname);
+	} elsif ($state == 9 and /^\s+(.+?):\d+(?:\.\d+)+->(\d+(?:\.\d+)+)\(DEAD\)\s*$/) {
+		my $fn = $1;
+		my $rev = $2;
+		$fn =~ s#^/+##;
+		push @commit_revisions, [$fn, $rev];
+		push(@old,$fn);
+		print "Delete $fn\n" if $opt_v;
+	} elsif ($state == 9 and /^\s*$/) {
+		$state = 10;
+	} elsif (($state == 9 or $state == 10) and /^-+$/) {
+		$commitcount++;
+		if ($opt_L && $commitcount > $opt_L) {
+			last;
+		}
+		commit();
+		if (($commitcount & 1023) == 0) {
+			system(qw(git repack -a -d));
+		}
+		$state = 1;
+	} elsif ($state == 11 and /^-+$/) {
+		$state = 1;
+	} elsif (/^-+$/) { # end of unknown-line processing
+		$state = 1;
+	} elsif ($state != 11) { # ignore stuff when skipping
+		print STDERR "* UNKNOWN LINE * $_\n";
+	}
+}
+commit() if $branch and $state != 11;
+
+unless ($opt_P) {
+	unlink($cvspsfile);
+}
+
+# The heuristic of repacking every 1024 commits can leave a
+# lot of unpacked data.  If there is more than 1MB worth of
+# not-packed objects, repack once more.
+my $line = `git count-objects`;
+if ($line =~ /^(\d+) objects, (\d+) kilobytes$/) {
+  my ($n_objects, $kb) = ($1, $2);
+  1024 < $kb
+    and system(qw(git repack -a -d));
+}
+
+foreach my $git_index (values %index) {
+    if ($git_index ne "$git_dir/index") {
+	unlink($git_index);
+    }
+}
+
+if (defined $orig_git_index) {
+	$ENV{GIT_INDEX_FILE} = $orig_git_index;
+} else {
+	delete $ENV{GIT_INDEX_FILE};
+}
+
+# Now switch back to the branch we were in before all of this happened
+if ($orig_branch) {
+	print "DONE.\n" if $opt_v;
+	if ($opt_i) {
+		exit 0;
+	}
+	my $tip_at_end = `git rev-parse --verify HEAD`;
+	if ($tip_at_start ne $tip_at_end) {
+		for ($tip_at_start, $tip_at_end) { chomp; }
+		print "Fetched into the current branch.\n" if $opt_v;
+		system(qw(git read-tree -u -m),
+		       $tip_at_start, $tip_at_end);
+		die "Fast-forward update failed: $?\n" if $?;
+	}
+	else {
+		system(qw(git merge cvsimport HEAD), "$remote/$opt_o");
+		die "Could not merge $opt_o into the current branch.\n" if $?;
+	}
+} else {
+	$orig_branch = "master";
+	print "DONE; creating $orig_branch branch\n" if $opt_v;
+	system("git", "update-ref", "refs/heads/master", "$remote/$opt_o")
+		unless defined get_headref('refs/heads/master');
+	system("git", "symbolic-ref", "$remote/HEAD", "$remote/$opt_o")
+		if ($opt_r && $opt_o ne 'HEAD');
+	system('git', 'update-ref', 'HEAD', "$orig_branch");
+	unless ($opt_i) {
+		system(qw(git checkout -f));
+		die "checkout failed: $?\n" if $?;
+	}
+}
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
deleted file mode 100755
index ad460a5..0000000
--- a/git-cvsimport.perl
+++ /dev/null
@@ -1,1179 +0,0 @@
-#!/usr/bin/perl
-
-# This tool is copyright (c) 2005, Matthias Urlichs.
-# It is released under the Gnu Public License, version 2.
-#
-# The basic idea is to aggregate CVS check-ins into related changes.
-# Fortunately, "cvsps" does that for us; all we have to do is to parse
-# its output.
-#
-# Checking out the files is done by a single long-running CVS connection
-# / server process.
-#
-# The head revision is on branch "origin" by default.
-# You can change that with the '-o' option.
-
-use 5.008;
-use strict;
-use warnings;
-use Getopt::Long;
-use File::Spec;
-use File::Temp qw(tempfile tmpnam);
-use File::Path qw(mkpath);
-use File::Basename qw(basename dirname);
-use Time::Local;
-use IO::Socket;
-use IO::Pipe;
-use POSIX qw(strftime tzset dup2 ENOENT);
-use IPC::Open2;
-
-$SIG{'PIPE'}="IGNORE";
-set_timezone('UTC');
-
-our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r, $opt_R);
-my (%conv_author_name, %conv_author_email, %conv_author_tz);
-
-my $cvsps2 = "@@CVSPS2_PATH@@";
-
-sub usage(;$) {
-	my $msg = shift;
-	print(STDERR "Error: $msg\n") if $msg;
-	print STDERR <<END;
-Usage: git cvsimport     # fetch/update GIT from CVS
-       [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
-       [-p opts-for-cvsps] [-P file] [-C GIT_repository] [-z fuzz] [-i] [-k]
-       [-u] [-s subst] [-a] [-m] [-M regex] [-S regex] [-L commitlimit]
-       [-r remote] [-R] [CVS_module]
-END
-	exit(1);
-}
-
-sub read_author_info($) {
-	my ($file) = @_;
-	my $user;
-	open my $f, '<', "$file" or die("Failed to open $file: $!\n");
-
-	while (<$f>) {
-		# Expected format is this:
-		#   exon=Andreas Ericsson <ae@op5.se>
-		if (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/) {
-			$user = $1;
-			$conv_author_name{$user} = $2;
-			$conv_author_email{$user} = $3;
-		}
-		# or with an optional timezone:
-		#   spawn=Simon Pawn <spawn@frog-pond.org> America/Chicago
-		elsif (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*(\S+?)\s*$/) {
-			$user = $1;
-			$conv_author_name{$user} = $2;
-			$conv_author_email{$user} = $3;
-			$conv_author_tz{$user} = $4;
-		}
-		# However, we also read from CVSROOT/users format
-		# to ease migration.
-		elsif (/^(\w+):(['"]?)(.+?)\2\s*$/) {
-			my $mapped;
-			($user, $mapped) = ($1, $3);
-			if ($mapped =~ /^\s*(.*?)\s*<(.*)>\s*$/) {
-				$conv_author_name{$user} = $1;
-				$conv_author_email{$user} = $2;
-			}
-			elsif ($mapped =~ /^<?(.*)>?$/) {
-				$conv_author_name{$user} = $user;
-				$conv_author_email{$user} = $1;
-			}
-		}
-		# NEEDSWORK: Maybe warn on unrecognized lines?
-	}
-	close ($f);
-}
-
-sub write_author_info($) {
-	my ($file) = @_;
-	open my $f, '>', $file or
-	  die("Failed to open $file for writing: $!");
-
-	foreach (keys %conv_author_name) {
-		print $f "$_=$conv_author_name{$_} <$conv_author_email{$_}>";
-		print $f " $conv_author_tz{$_}" if ($conv_author_tz{$_});
-		print $f "\n";
-	}
-	close ($f);
-}
-
-# Versions of perl before 5.10.0 may not automatically check $TZ each
-# time localtime is run (most platforms will do so only the first time).
-# We can work around this by using tzset() to update the internal
-# variable whenever we change the environment.
-sub set_timezone {
-	$ENV{TZ} = shift;
-	tzset();
-}
-
-# convert getopts specs for use by git config
-my %longmap = (
-	'A:' => 'authors-file',
-	'M:' => 'merge-regex',
-	'P:' => undef,
-	'R' => 'track-revisions',
-	'S:' => 'ignore-paths',
-);
-
-sub read_repo_config {
-	# Split the string between characters, unless there is a ':'
-	# So "abc:de" becomes ["a", "b", "c:", "d", "e"]
-	my @opts = split(/ *(?!:)/, shift);
-	foreach my $o (@opts) {
-		my $key = $o;
-		$key =~ s/://g;
-		my $arg = 'git config';
-		$arg .= ' --bool' if ($o !~ /:$/);
-		my $ckey = $key;
-
-		if (exists $longmap{$o}) {
-			# An uppercase option like -R cannot be
-			# expressed in the configuration, as the
-			# variable names are downcased.
-			$ckey = $longmap{$o};
-			next if (! defined $ckey);
-			$ckey =~ s/-//g;
-		}
-		chomp(my $tmp = `$arg --get cvsimport.$ckey`);
-		if ($tmp && !($arg =~ /--bool/ && $tmp eq 'false')) {
-			no strict 'refs';
-			my $opt_name = "opt_" . $key;
-			if (!$$opt_name) {
-				$$opt_name = $tmp;
-			}
-		}
-	}
-}
-
-my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:R";
-read_repo_config($opts);
-Getopt::Long::Configure( 'no_ignore_case', 'bundling' );
-
-# turn the Getopt::Std specification in a Getopt::Long one,
-# with support for multiple -M options
-GetOptions( map { s/:/=s/; /M/ ? "$_\@" : $_ } split( /(?!:)/, $opts ) )
-    or usage();
-usage if $opt_h;
-
-if (@ARGV == 0) {
-		chomp(my $module = `git config --get cvsimport.module`);
-		push(@ARGV, $module) if $? == 0;
-}
-@ARGV <= 1 or usage("You can't specify more than one CVS module");
-
-if ($opt_d) {
-	$ENV{"CVSROOT"} = $opt_d;
-} elsif (-f 'CVS/Root') {
-	open my $f, '<', 'CVS/Root' or die 'Failed to open CVS/Root';
-	$opt_d = <$f>;
-	chomp $opt_d;
-	close $f;
-	$ENV{"CVSROOT"} = $opt_d;
-} elsif ($ENV{"CVSROOT"}) {
-	$opt_d = $ENV{"CVSROOT"};
-} else {
-	usage("CVSROOT needs to be set");
-}
-$opt_s ||= "-";
-$opt_a ||= 0;
-
-my $git_tree = $opt_C;
-$git_tree ||= ".";
-
-my $remote;
-if (defined $opt_r) {
-	$remote = 'refs/remotes/' . $opt_r;
-	$opt_o ||= "master";
-} else {
-	$opt_o ||= "origin";
-	$remote = 'refs/heads';
-}
-
-my $cvs_tree;
-if ($#ARGV == 0) {
-	$cvs_tree = $ARGV[0];
-} elsif (-f 'CVS/Repository') {
-	open my $f, '<', 'CVS/Repository' or
-	    die 'Failed to open CVS/Repository';
-	$cvs_tree = <$f>;
-	chomp $cvs_tree;
-	close $f;
-} else {
-	usage("CVS module has to be specified");
-}
-
-our @mergerx = ();
-if ($opt_m) {
-	@mergerx = ( qr/\b(?:from|of|merge|merging|merged) ([-\w]+)/i );
-}
-if (@opt_M) {
-	push (@mergerx, map { qr/$_/ } @opt_M);
-}
-
-# Remember UTC of our starting time
-# we'll want to avoid importing commits
-# that are too recent
-our $starttime = time();
-
-select(STDERR); $|=1; select(STDOUT);
-
-
-package CVSconn;
-# Basic CVS dialog.
-# We're only interested in connecting and downloading, so ...
-
-use File::Spec;
-use File::Temp qw(tempfile);
-use POSIX qw(strftime dup2);
-
-sub new {
-	my ($what,$repo,$subdir) = @_;
-	$what=ref($what) if ref($what);
-
-	my $self = {};
-	$self->{'buffer'} = "";
-	bless($self,$what);
-
-	$repo =~ s#/+$##;
-	$self->{'fullrep'} = $repo;
-	$self->conn();
-
-	$self->{'subdir'} = $subdir;
-	$self->{'lines'} = undef;
-
-	return $self;
-}
-
-sub find_password_entry {
-	my ($cvspass, @cvsroot) = @_;
-	my ($file, $delim) = @$cvspass;
-	my $pass;
-	local ($_);
-
-	if (open(my $fh, $file)) {
-		# :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
-		CVSPASSFILE:
-		while (<$fh>) {
-			chomp;
-			s/^\/\d+\s+//;
-			my ($w, $p) = split($delim,$_,2);
-			for my $cvsroot (@cvsroot) {
-				if ($w eq $cvsroot) {
-					$pass = $p;
-					last CVSPASSFILE;
-				}
-			}
-		}
-		close($fh);
-	}
-	return $pass;
-}
-
-sub conn {
-	my $self = shift;
-	my $repo = $self->{'fullrep'};
-	if ($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) {
-		my ($param,$user,$pass,$serv,$port) = ($1,$2,$3,$4,$5);
-
-		my ($proxyhost,$proxyport);
-		if ($param && ($param =~ m/proxy=([^;]+)/)) {
-			$proxyhost = $1;
-			# Default proxyport, if not specified, is 8080.
-			$proxyport = 8080;
-			if ($ENV{"CVS_PROXY_PORT"}) {
-				$proxyport = $ENV{"CVS_PROXY_PORT"};
-			}
-			if ($param =~ m/proxyport=([^;]+)/) {
-				$proxyport = $1;
-			}
-		}
-		$repo ||= '/';
-
-		# if username is not explicit in CVSROOT, then use current user, as cvs would
-		$user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user;
-		my $rr2 = "-";
-		unless ($port) {
-			$rr2 = ":pserver:$user\@$serv:$repo";
-			$port=2401;
-		}
-		my $rr = ":pserver:$user\@$serv:$port$repo";
-
-		if ($pass) {
-			$pass = $self->_scramble($pass);
-		} else {
-			my @cvspass = ([$ENV{'HOME'}."/.cvspass", qr/\s/],
-				       [$ENV{'HOME'}."/.cvs/cvspass", qr/=/]);
-			my @loc = ();
-			foreach my $cvspass (@cvspass) {
-				my $p = find_password_entry($cvspass, $rr, $rr2);
-				if ($p) {
-					push @loc, $cvspass->[0];
-					$pass = $p;
-				}
-			}
-
-			if (1 < @loc) {
-				die("Multiple cvs password files have ".
-				    "entries for CVSROOT $opt_d: @loc");
-			} elsif (!$pass) {
-				$pass = "A";
-			}
-		}
-
-		my ($s, $rep);
-		if ($proxyhost) {
-
-			# Use a HTTP Proxy. Only works for HTTP proxies that
-			# don't require user authentication
-			#
-			# See: http://www.ietf.org/rfc/rfc2817.txt
-
-			$s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport);
-			die "Socket to $proxyhost: $!\n" unless defined $s;
-			$s->write("CONNECT $serv:$port HTTP/1.1\r\nHost: $serv:$port\r\n\r\n")
-	                        or die "Write to $proxyhost: $!\n";
-	                $s->flush();
-
-			$rep = <$s>;
-
-			# The answer should look like 'HTTP/1.x 2yy ....'
-			if (!($rep =~ m#^HTTP/1\.. 2[0-9][0-9]#)) {
-				die "Proxy connect: $rep\n";
-			}
-			# Skip up to the empty line of the proxy server output
-			# including the response headers.
-			while ($rep = <$s>) {
-				last if (!defined $rep ||
-					 $rep eq "\n" ||
-					 $rep eq "\r\n");
-			}
-		} else {
-			$s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port);
-			die "Socket to $serv: $!\n" unless defined $s;
-		}
-
-		$s->write("BEGIN AUTH REQUEST\n$repo\n$user\n$pass\nEND AUTH REQUEST\n")
-			or die "Write to $serv: $!\n";
-		$s->flush();
-
-		$rep = <$s>;
-
-		if ($rep ne "I LOVE YOU\n") {
-			$rep="<unknown>" unless $rep;
-			die "AuthReply: $rep\n";
-		}
-		$self->{'socketo'} = $s;
-		$self->{'socketi'} = $s;
-	} else { # local or ext: Fork off our own cvs server.
-		my $pr = IO::Pipe->new();
-		my $pw = IO::Pipe->new();
-		my $pid = fork();
-		die "Fork: $!\n" unless defined $pid;
-		my $cvs = 'cvs';
-		$cvs = $ENV{CVS_SERVER} if exists $ENV{CVS_SERVER};
-		my $rsh = 'rsh';
-		$rsh = $ENV{CVS_RSH} if exists $ENV{CVS_RSH};
-
-		my @cvs = ($cvs, 'server');
-		my ($local, $user, $host);
-		$local = $repo =~ s/:local://;
-		if (!$local) {
-		    $repo =~ s/:ext://;
-		    $local = !($repo =~ s/^(?:([^\@:]+)\@)?([^:]+)://);
-		    ($user, $host) = ($1, $2);
-		}
-		if (!$local) {
-		    if ($user) {
-			unshift @cvs, $rsh, '-l', $user, $host;
-		    } else {
-			unshift @cvs, $rsh, $host;
-		    }
-		}
-
-		unless ($pid) {
-			$pr->writer();
-			$pw->reader();
-			dup2($pw->fileno(),0);
-			dup2($pr->fileno(),1);
-			$pr->close();
-			$pw->close();
-			exec(@cvs);
-		}
-		$pw->writer();
-		$pr->reader();
-		$self->{'socketo'} = $pw;
-		$self->{'socketi'} = $pr;
-	}
-	$self->{'socketo'}->write("Root $repo\n");
-
-	# Trial and error says that this probably is the minimum set
-	$self->{'socketo'}->write("Valid-responses ok error Valid-requests Mode M Mbinary E Checked-in Created Updated Merged Removed\n");
-
-	$self->{'socketo'}->write("valid-requests\n");
-	$self->{'socketo'}->flush();
-
-	my $rep=$self->readline();
-	die "Failed to read from server" unless defined $rep;
-	chomp($rep);
-	if ($rep !~ s/^Valid-requests\s*//) {
-		$rep="<unknown>" unless $rep;
-		die "Expected Valid-requests from server, but got: $rep\n";
-	}
-	chomp(my $res=$self->readline());
-	die "validReply: $res\n" if $res ne "ok";
-
-	$self->{'socketo'}->write("UseUnchanged\n") if $rep =~ /\bUseUnchanged\b/;
-	$self->{'repo'} = $repo;
-}
-
-sub readline {
-	my ($self) = @_;
-	return $self->{'socketi'}->getline();
-}
-
-sub _file {
-	# Request a file with a given revision.
-	# Trial and error says this is a good way to do it. :-/
-	my ($self,$fn,$rev) = @_;
-	$self->{'socketo'}->write("Argument -N\n") or return undef;
-	$self->{'socketo'}->write("Argument -P\n") or return undef;
-	# -kk: Linus' version doesn't use it - defaults to off
-	if ($opt_k) {
-	    $self->{'socketo'}->write("Argument -kk\n") or return undef;
-	}
-	$self->{'socketo'}->write("Argument -r\n") or return undef;
-	$self->{'socketo'}->write("Argument $rev\n") or return undef;
-	$self->{'socketo'}->write("Argument --\n") or return undef;
-	$self->{'socketo'}->write("Argument $self->{'subdir'}/$fn\n") or return undef;
-	$self->{'socketo'}->write("Directory .\n") or return undef;
-	$self->{'socketo'}->write("$self->{'repo'}\n") or return undef;
-	# $self->{'socketo'}->write("Sticky T1.0\n") or return undef;
-	$self->{'socketo'}->write("co\n") or return undef;
-	$self->{'socketo'}->flush() or return undef;
-	$self->{'lines'} = 0;
-	return 1;
-}
-sub _line {
-	# Read a line from the server.
-	# ... except that 'line' may be an entire file. ;-)
-	my ($self, $fh) = @_;
-	die "Not in lines" unless defined $self->{'lines'};
-
-	my $line;
-	my $res=0;
-	while (defined($line = $self->readline())) {
-		# M U gnupg-cvs-rep/AUTHORS
-		# Updated gnupg-cvs-rep/
-		# /daten/src/rsync/gnupg-cvs-rep/AUTHORS
-		# /AUTHORS/1.1///T1.1
-		# u=rw,g=rw,o=rw
-		# 0
-		# ok
-
-		if ($line =~ s/^(?:Created|Updated) //) {
-			$line = $self->readline(); # path
-			$line = $self->readline(); # Entries line
-			my $mode = $self->readline(); chomp $mode;
-			$self->{'mode'} = $mode;
-			defined (my $cnt = $self->readline())
-				or die "EOF from server after 'Changed'\n";
-			chomp $cnt;
-			die "Duh: Filesize $cnt" if $cnt !~ /^\d+$/;
-			$line="";
-			$res = $self->_fetchfile($fh, $cnt);
-		} elsif ($line =~ s/^ //) {
-			print $fh $line;
-			$res += length($line);
-		} elsif ($line =~ /^M\b/) {
-			# output, do nothing
-		} elsif ($line =~ /^Mbinary\b/) {
-			my $cnt;
-			die "EOF from server after 'Mbinary'" unless defined ($cnt = $self->readline());
-			chomp $cnt;
-			die "Duh: Mbinary $cnt" if $cnt !~ /^\d+$/ or $cnt<1;
-			$line="";
-			$res += $self->_fetchfile($fh, $cnt);
-		} else {
-			chomp $line;
-			if ($line eq "ok") {
-				# print STDERR "S: ok (".length($res).")\n";
-				return $res;
-			} elsif ($line =~ s/^E //) {
-				# print STDERR "S: $line\n";
-			} elsif ($line =~ /^(Remove-entry|Removed) /i) {
-				$line = $self->readline(); # filename
-				$line = $self->readline(); # OK
-				chomp $line;
-				die "Unknown: $line" if $line ne "ok";
-				return -1;
-			} else {
-				die "Unknown: $line\n";
-			}
-		}
-	}
-	return undef;
-}
-sub file {
-	my ($self,$fn,$rev) = @_;
-	my $res;
-
-	my ($fh, $name) = tempfile('gitcvs.XXXXXX',
-		    DIR => File::Spec->tmpdir(), UNLINK => 1);
-
-	$self->_file($fn,$rev) and $res = $self->_line($fh);
-
-	if (!defined $res) {
-	    print STDERR "Server has gone away while fetching $fn $rev, retrying...\n";
-	    truncate $fh, 0;
-	    $self->conn();
-	    $self->_file($fn,$rev) or die "No file command send";
-	    $res = $self->_line($fh);
-	    die "Retry failed" unless defined $res;
-	}
-	close ($fh);
-
-	return ($name, $res);
-}
-sub _fetchfile {
-	my ($self, $fh, $cnt) = @_;
-	my $res = 0;
-	my $bufsize = 1024 * 1024;
-	while ($cnt) {
-	    if ($bufsize > $cnt) {
-		$bufsize = $cnt;
-	    }
-	    my $buf;
-	    my $num = $self->{'socketi'}->read($buf,$bufsize);
-	    die "Server: Filesize $cnt: $num: $!\n" if not defined $num or $num<=0;
-	    print $fh $buf;
-	    $res += $num;
-	    $cnt -= $num;
-	}
-	return $res;
-}
-
-sub _scramble {
-	my ($self, $pass) = @_;
-	my $scrambled = "A";
-
-	return $scrambled unless $pass;
-
-	my $pass_len = length($pass);
-	my @pass_arr = split("", $pass);
-	my $i;
-
-	# from cvs/src/scramble.c
-	my @shifts = (
-		  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
-		 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-		114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
-		111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
-		 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
-		125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
-		 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
-		 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
-		225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
-		199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
-		174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
-		207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
-		192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
-		227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
-		182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
-		243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
-	);
-
-	for ($i = 0; $i < $pass_len; $i++) {
-		$scrambled .= pack("C", $shifts[ord($pass_arr[$i])]);
-	}
-
-	return $scrambled;
-}
-
-package main;
-
-my $cvs = CVSconn->new($opt_d, $cvs_tree);
-
-
-sub pdate($) {
-	my ($d) = @_;
-	m#(\d{2,4})/(\d\d)/(\d\d)\s(\d\d):(\d\d)(?::(\d\d))?#
-		or die "Unparseable date: $d\n";
-	my $y=$1; $y-=1900 if $y>1900;
-	return timegm($6||0,$5,$4,$3,$2-1,$y);
-}
-
-sub pmode($) {
-	my ($mode) = @_;
-	my $m = 0;
-	my $mm = 0;
-	my $um = 0;
-	for my $x(split(//,$mode)) {
-		if ($x eq ",") {
-			$m |= $mm&$um;
-			$mm = 0;
-			$um = 0;
-		} elsif ($x eq "u") { $um |= 0700;
-		} elsif ($x eq "g") { $um |= 0070;
-		} elsif ($x eq "o") { $um |= 0007;
-		} elsif ($x eq "r") { $mm |= 0444;
-		} elsif ($x eq "w") { $mm |= 0222;
-		} elsif ($x eq "x") { $mm |= 0111;
-		} elsif ($x eq "=") { # do nothing
-		} else { die "Unknown mode: $mode\n";
-		}
-	}
-	$m |= $mm&$um;
-	return $m;
-}
-
-sub getwd() {
-	my $pwd = `pwd`;
-	chomp $pwd;
-	return $pwd;
-}
-
-sub is_sha1 {
-	my $s = shift;
-	return $s =~ /^[a-f0-9]{40}$/;
-}
-
-sub get_headref ($) {
-	my $name = shift;
-	my $r = `git rev-parse --verify '$name' 2>/dev/null`;
-	return undef unless $? == 0;
-	chomp $r;
-	return $r;
-}
-
-my $user_filename_prepend = '';
-sub munge_user_filename {
-	my $name = shift;
-	return File::Spec->file_name_is_absolute($name) ?
-		$name :
-		$user_filename_prepend . $name;
-}
-
--d $git_tree
-	or mkdir($git_tree,0777)
-	or die "Could not create $git_tree: $!";
-if ($git_tree ne '.') {
-	$user_filename_prepend = getwd() . '/';
-	chdir($git_tree);
-}
-
-my $last_branch = "";
-my $orig_branch = "";
-my %branch_date;
-my $tip_at_start = undef;
-
-my $git_dir = $ENV{"GIT_DIR"} || ".git";
-$git_dir = getwd()."/".$git_dir unless $git_dir =~ m#^/#;
-$ENV{"GIT_DIR"} = $git_dir;
-my $orig_git_index;
-$orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE};
-
-my %index; # holds filenames of one index per branch
-
-unless (-d $git_dir) {
-	system(qw(git init));
-	die "Cannot init the GIT db at $git_tree: $?\n" if $?;
-	system(qw(git read-tree --empty));
-	die "Cannot init an empty tree: $?\n" if $?;
-
-	$last_branch = $opt_o;
-	$orig_branch = "";
-} else {
-	open(F, "-|", qw(git symbolic-ref HEAD)) or
-		die "Cannot run git symbolic-ref: $!\n";
-	chomp ($last_branch = <F>);
-	$last_branch = basename($last_branch);
-	close(F);
-	unless ($last_branch) {
-		warn "Cannot read the last branch name: $! -- assuming 'master'\n";
-		$last_branch = "master";
-	}
-	$orig_branch = $last_branch;
-	$tip_at_start = `git rev-parse --verify HEAD`;
-
-	# Get the last import timestamps
-	my $fmt = '($ref, $author) = (%(refname), %(author));';
-	my @cmd = ('git', 'for-each-ref', '--perl', "--format=$fmt", $remote);
-	open(H, "-|", @cmd) or die "Cannot run git for-each-ref: $!\n";
-	while (defined(my $entry = <H>)) {
-		my ($ref, $author);
-		eval($entry) || die "cannot eval refs list: $@";
-		my ($head) = ($ref =~ m|^$remote/(.*)|);
-		$author =~ /^.*\s(\d+)\s[-+]\d{4}$/;
-		$branch_date{$head} = $1;
-	}
-	close(H);
-        if (!exists $branch_date{$opt_o}) {
-		die "Branch '$opt_o' does not exist.\n".
-		       "Either use the correct '-o branch' option,\n".
-		       "or import to a new repository.\n";
-        }
-}
-
--d $git_dir
-	or die "Could not create git subdir ($git_dir).\n";
-
-# now we read (and possibly save) author-info as well
--f "$git_dir/cvs-authors" and
-  read_author_info("$git_dir/cvs-authors");
-if ($opt_A) {
-	read_author_info(munge_user_filename($opt_A));
-	write_author_info("$git_dir/cvs-authors");
-}
-
-# open .git/cvs-revisions, if requested
-open my $revision_map, '>>', "$git_dir/cvs-revisions"
-    or die "Can't open $git_dir/cvs-revisions for appending: $!\n"
-	if defined $opt_R;
-
-
-#
-# run cvsps into a file unless we are getting
-# it passed as a file via $opt_P
-#
-my $cvspsfile;
-unless ($opt_P) {
-	print "Running cvsps...\n" if $opt_v;
-	my $pid = open(CVSPS,"-|");
-	my $cvspsfh;
-	die "Cannot fork: $!\n" unless defined $pid;
-	unless ($pid) {
-		my @opt;
-		@opt = split(/,/,$opt_p) if defined $opt_p;
-		unshift @opt, '-z', $opt_z if defined $opt_z;
-		unshift @opt, '-q'         unless defined $opt_v;
-		unless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {
-			push @opt, '--cvs-direct';
-		}
-		exec($cvsps2,"--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
-		die "Could not start cvsps: $!\n";
-	}
-	($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
-					  DIR => File::Spec->tmpdir());
-	while (<CVSPS>) {
-	    print $cvspsfh $_;
-	}
-	close CVSPS;
-	$? == 0 or die "git cvsimport: fatal: cvsps reported error\n";
-	close $cvspsfh;
-} else {
-	$cvspsfile = munge_user_filename($opt_P);
-}
-
-open(CVS, "<$cvspsfile") or die $!;
-
-## cvsps output:
-#---------------------
-#PatchSet 314
-#Date: 1999/09/18 13:03:59
-#Author: wkoch
-#Branch: STABLE-BRANCH-1-0
-#Ancestor branch: HEAD
-#Tag: (none)
-#Log:
-#    See ChangeLog: Sat Sep 18 13:03:28 CEST 1999  Werner Koch
-#Members:
-#	README:1.57->1.57.2.1
-#	VERSION:1.96->1.96.2.1
-#
-#---------------------
-
-my $state = 0;
-
-sub update_index (\@\@) {
-	my $old = shift;
-	my $new = shift;
-	open(my $fh, '|-', qw(git update-index -z --index-info))
-		or die "unable to open git update-index: $!";
-	print $fh
-		(map { "0 0000000000000000000000000000000000000000\t$_\0" }
-			@$old),
-		(map { '100' . sprintf('%o', $_->[0]) . " $_->[1]\t$_->[2]\0" }
-			@$new)
-		or die "unable to write to git update-index: $!";
-	close $fh
-		or die "unable to write to git update-index: $!";
-	$? and die "git update-index reported error: $?";
-}
-
-sub write_tree () {
-	open(my $fh, '-|', qw(git write-tree))
-		or die "unable to open git write-tree: $!";
-	chomp(my $tree = <$fh>);
-	is_sha1($tree)
-		or die "Cannot get tree id ($tree): $!";
-	close($fh)
-		or die "Error running git write-tree: $?\n";
-	print "Tree ID $tree\n" if $opt_v;
-	return $tree;
-}
-
-my ($patchset,$date,$author_name,$author_email,$author_tz,$branch,$ancestor,$tag,$logmsg);
-my (@old,@new,@skipped,%ignorebranch,@commit_revisions);
-
-# commits that cvsps cannot place anywhere...
-$ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
-
-sub commit {
-	if ($branch eq $opt_o && !$index{branch} &&
-		!get_headref("$remote/$branch")) {
-	    # looks like an initial commit
-	    # use the index primed by git init
-	    $ENV{GIT_INDEX_FILE} = "$git_dir/index";
-	    $index{$branch} = "$git_dir/index";
-	} else {
-	    # use an index per branch to speed up
-	    # imports of projects with many branches
-	    unless ($index{$branch}) {
-		$index{$branch} = tmpnam();
-		$ENV{GIT_INDEX_FILE} = $index{$branch};
-		if ($ancestor) {
-		    system("git", "read-tree", "$remote/$ancestor");
-		} else {
-		    system("git", "read-tree", "$remote/$branch");
-		}
-		die "read-tree failed: $?\n" if $?;
-	    }
-	}
-        $ENV{GIT_INDEX_FILE} = $index{$branch};
-
-	update_index(@old, @new);
-	@old = @new = ();
-	my $tree = write_tree();
-	my $parent = get_headref("$remote/$last_branch");
-	print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v;
-
-	my @commit_args;
-	push @commit_args, ("-p", $parent) if $parent;
-
-	# loose detection of merges
-	# based on the commit msg
-	foreach my $rx (@mergerx) {
-		next unless $logmsg =~ $rx && $1;
-		my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
-		if (my $sha1 = get_headref("$remote/$mparent")) {
-			push @commit_args, '-p', "$remote/$mparent";
-			print "Merge parent branch: $mparent\n" if $opt_v;
-		}
-	}
-
-	set_timezone($author_tz);
-	my $commit_date = strftime("%s %z", localtime($date));
-	set_timezone('UTC');
-	$ENV{GIT_AUTHOR_NAME} = $author_name;
-	$ENV{GIT_AUTHOR_EMAIL} = $author_email;
-	$ENV{GIT_AUTHOR_DATE} = $commit_date;
-	$ENV{GIT_COMMITTER_NAME} = $author_name;
-	$ENV{GIT_COMMITTER_EMAIL} = $author_email;
-	$ENV{GIT_COMMITTER_DATE} = $commit_date;
-	my $pid = open2(my $commit_read, my $commit_write,
-		'git', 'commit-tree', $tree, @commit_args);
-
-	# compatibility with git2cvs
-	substr($logmsg,32767) = "" if length($logmsg) > 32767;
-	$logmsg =~ s/[\s\n]+\z//;
-
-	if (@skipped) {
-	    $logmsg .= "\n\n\nSKIPPED:\n\t";
-	    $logmsg .= join("\n\t", @skipped) . "\n";
-	    @skipped = ();
-	}
-
-	print($commit_write "$logmsg\n") && close($commit_write)
-		or die "Error writing to git commit-tree: $!\n";
-
-	print "Committed patch $patchset ($branch $commit_date)\n" if $opt_v;
-	chomp(my $cid = <$commit_read>);
-	is_sha1($cid) or die "Cannot get commit id ($cid): $!\n";
-	print "Commit ID $cid\n" if $opt_v;
-	close($commit_read);
-
-	waitpid($pid,0);
-	die "Error running git commit-tree: $?\n" if $?;
-
-	system('git' , 'update-ref', "$remote/$branch", $cid) == 0
-		or die "Cannot write branch $branch for update: $!\n";
-
-	if ($revision_map) {
-		print $revision_map "@$_ $cid\n" for @commit_revisions;
-	}
-	@commit_revisions = ();
-
-	if ($tag) {
-	        my ($xtag) = $tag;
-		$xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **
-		$xtag =~ tr/_/\./ if ( $opt_u );
-		$xtag =~ s/[\/]/$opt_s/g;
-
-		# See refs.c for these rules.
-		# Tag cannot contain bad chars. (See bad_ref_char in refs.c.)
-		$xtag =~ s/[ ~\^:\\\*\?\[]//g;
-		# Other bad strings for tags:
-		# (See check_refname_component in refs.c.)
-		1 while $xtag =~ s/
-			(?: \.\.        # Tag cannot contain '..'.
-			|   \@{         # Tag cannot contain '@{'.
-			| ^ -           # Tag cannot begin with '-'.
-			|   \.lock $    # Tag cannot end with '.lock'.
-			| ^ \.          # Tag cannot begin...
-			|   \. $        # ...or end with '.'
-			)//xg;
-		# Tag cannot be empty.
-		if ($xtag eq '') {
-			warn("warning: ignoring tag '$tag'",
-			" with invalid tagname\n");
-			return;
-		}
-
-		if (system('git' , 'tag', '-f', $xtag, $cid) != 0) {
-			# We did our best to sanitize the tag, but still failed
-			# for whatever reason. Bail out, and give the user
-			# enough information to understand if/how we should
-			# improve the translation in the future.
-			if ($tag ne $xtag) {
-				print "Translated '$tag' tag to '$xtag'\n";
-			}
-			die "Cannot create tag $xtag: $!\n";
-		}
-
-		print "Created tag '$xtag' on '$branch'\n" if $opt_v;
-	}
-};
-
-my $commitcount = 1;
-while (<CVS>) {
-	chomp;
-	if ($state == 0 and /^-+$/) {
-		$state = 1;
-	} elsif ($state == 0) {
-		$state = 1;
-		redo;
-	} elsif (($state==0 or $state==1) and s/^PatchSet\s+//) {
-		$patchset = 0+$_;
-		$state=2;
-	} elsif ($state == 2 and s/^Date:\s+//) {
-		$date = pdate($_);
-		unless ($date) {
-			print STDERR "Could not parse date: $_\n";
-			$state=0;
-			next;
-		}
-		$state=3;
-	} elsif ($state == 3 and s/^Author:\s+//) {
-		$author_tz = "UTC";
-		s/\s+$//;
-		if (/^(.*?)\s+<(.*)>/) {
-		    ($author_name, $author_email) = ($1, $2);
-		} elsif ($conv_author_name{$_}) {
-			$author_name = $conv_author_name{$_};
-			$author_email = $conv_author_email{$_};
-			$author_tz = $conv_author_tz{$_} if ($conv_author_tz{$_});
-		} else {
-		    $author_name = $author_email = $_;
-		}
-		$state = 4;
-	} elsif ($state == 4 and s/^Branch:\s+//) {
-		s/\s+$//;
-		tr/_/\./ if ( $opt_u );
-		s/[\/]/$opt_s/g;
-		$branch = $_;
-		$state = 5;
-	} elsif ($state == 5 and s/^Ancestor branch:\s+//) {
-		s/\s+$//;
-		$ancestor = $_;
-		$ancestor = $opt_o if $ancestor eq "HEAD";
-		$state = 6;
-	} elsif ($state == 5) {
-		$ancestor = undef;
-		$state = 6;
-		redo;
-	} elsif ($state == 6 and s/^Tag:\s+//) {
-		s/\s+$//;
-		if ($_ eq "(none)") {
-			$tag = undef;
-		} else {
-			$tag = $_;
-		}
-		$state = 7;
-	} elsif ($state == 7 and /^Log:/) {
-		$logmsg = "";
-		$state = 8;
-	} elsif ($state == 8 and /^Members:/) {
-		$branch = $opt_o if $branch eq "HEAD";
-		if (defined $branch_date{$branch} and $branch_date{$branch} >= $date) {
-			# skip
-			print "skip patchset $patchset: $date before $branch_date{$branch}\n" if $opt_v;
-			$state = 11;
-			next;
-		}
-		if (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {
-			# skip if the commit is too recent
-			# given that the cvsps default fuzz is 300s, we give ourselves another
-			# 300s just in case -- this also prevents skipping commits
-			# due to server clock drift
-			print "skip patchset $patchset: $date too recent\n" if $opt_v;
-			$state = 11;
-			next;
-		}
-		if (exists $ignorebranch{$branch}) {
-			print STDERR "Skipping $branch\n";
-			$state = 11;
-			next;
-		}
-		if ($ancestor) {
-			if ($ancestor eq $branch) {
-				print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n";
-				$ancestor = $opt_o;
-			}
-			if (defined get_headref("$remote/$branch")) {
-				print STDERR "Branch $branch already exists!\n";
-				$state=11;
-				next;
-			}
-			my $id = get_headref("$remote/$ancestor");
-			if (!$id) {
-				print STDERR "Branch $ancestor does not exist!\n";
-				$ignorebranch{$branch} = 1;
-				$state=11;
-				next;
-			}
-
-			system(qw(git update-ref -m cvsimport),
-				"$remote/$branch", $id);
-			if($? != 0) {
-				print STDERR "Could not create branch $branch\n";
-				$ignorebranch{$branch} = 1;
-				$state=11;
-				next;
-			}
-		}
-		$last_branch = $branch if $branch ne $last_branch;
-		$state = 9;
-	} elsif ($state == 8) {
-		$logmsg .= "$_\n";
-	} elsif ($state == 9 and /^\s+(.+?):(INITIAL|\d+(?:\.\d+)+)->(\d+(?:\.\d+)+)\s*$/) {
-#	VERSION:1.96->1.96.2.1
-		my $init = ($2 eq "INITIAL");
-		my $fn = $1;
-		my $rev = $3;
-		$fn =~ s#^/+##;
-		if ($opt_S && $fn =~ m/$opt_S/) {
-		    print "SKIPPING $fn v $rev\n";
-		    push(@skipped, $fn);
-		    next;
-		}
-		push @commit_revisions, [$fn, $rev];
-		print "Fetching $fn   v $rev\n" if $opt_v;
-		my ($tmpname, $size) = $cvs->file($fn,$rev);
-		if ($size == -1) {
-			push(@old,$fn);
-			print "Drop $fn\n" if $opt_v;
-		} else {
-			print "".($init ? "New" : "Update")." $fn: $size bytes\n" if $opt_v;
-			my $pid = open(my $F, '-|');
-			die $! unless defined $pid;
-			if (!$pid) {
-			    exec("git", "hash-object", "-w", $tmpname)
-				or die "Cannot create object: $!\n";
-			}
-			my $sha = <$F>;
-			chomp $sha;
-			close $F;
-			my $mode = pmode($cvs->{'mode'});
-			push(@new,[$mode, $sha, $fn]); # may be resurrected!
-		}
-		unlink($tmpname);
-	} elsif ($state == 9 and /^\s+(.+?):\d+(?:\.\d+)+->(\d+(?:\.\d+)+)\(DEAD\)\s*$/) {
-		my $fn = $1;
-		my $rev = $2;
-		$fn =~ s#^/+##;
-		push @commit_revisions, [$fn, $rev];
-		push(@old,$fn);
-		print "Delete $fn\n" if $opt_v;
-	} elsif ($state == 9 and /^\s*$/) {
-		$state = 10;
-	} elsif (($state == 9 or $state == 10) and /^-+$/) {
-		$commitcount++;
-		if ($opt_L && $commitcount > $opt_L) {
-			last;
-		}
-		commit();
-		if (($commitcount & 1023) == 0) {
-			system(qw(git repack -a -d));
-		}
-		$state = 1;
-	} elsif ($state == 11 and /^-+$/) {
-		$state = 1;
-	} elsif (/^-+$/) { # end of unknown-line processing
-		$state = 1;
-	} elsif ($state != 11) { # ignore stuff when skipping
-		print STDERR "* UNKNOWN LINE * $_\n";
-	}
-}
-commit() if $branch and $state != 11;
-
-unless ($opt_P) {
-	unlink($cvspsfile);
-}
-
-# The heuristic of repacking every 1024 commits can leave a
-# lot of unpacked data.  If there is more than 1MB worth of
-# not-packed objects, repack once more.
-my $line = `git count-objects`;
-if ($line =~ /^(\d+) objects, (\d+) kilobytes$/) {
-  my ($n_objects, $kb) = ($1, $2);
-  1024 < $kb
-    and system(qw(git repack -a -d));
-}
-
-foreach my $git_index (values %index) {
-    if ($git_index ne "$git_dir/index") {
-	unlink($git_index);
-    }
-}
-
-if (defined $orig_git_index) {
-	$ENV{GIT_INDEX_FILE} = $orig_git_index;
-} else {
-	delete $ENV{GIT_INDEX_FILE};
-}
-
-# Now switch back to the branch we were in before all of this happened
-if ($orig_branch) {
-	print "DONE.\n" if $opt_v;
-	if ($opt_i) {
-		exit 0;
-	}
-	my $tip_at_end = `git rev-parse --verify HEAD`;
-	if ($tip_at_start ne $tip_at_end) {
-		for ($tip_at_start, $tip_at_end) { chomp; }
-		print "Fetched into the current branch.\n" if $opt_v;
-		system(qw(git read-tree -u -m),
-		       $tip_at_start, $tip_at_end);
-		die "Fast-forward update failed: $?\n" if $?;
-	}
-	else {
-		system(qw(git merge cvsimport HEAD), "$remote/$opt_o");
-		die "Could not merge $opt_o into the current branch.\n" if $?;
-	}
-} else {
-	$orig_branch = "master";
-	print "DONE; creating $orig_branch branch\n" if $opt_v;
-	system("git", "update-ref", "refs/heads/master", "$remote/$opt_o")
-		unless defined get_headref('refs/heads/master');
-	system("git", "symbolic-ref", "$remote/HEAD", "$remote/$opt_o")
-		if ($opt_r && $opt_o ne 'HEAD');
-	system('git', 'update-ref', 'HEAD', "$orig_branch");
-	unless ($opt_i) {
-		system(qw(git checkout -f));
-		die "checkout failed: $?\n" if $?;
-	}
-}
diff --git a/git-cvsimport.sh b/git-cvsimport.sh
new file mode 100755
index 0000000..71ba11d
--- /dev/null
+++ b/git-cvsimport.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+GIT_CVSPS_VERSION=2
+
+exec git cvsimport-$GIT_CVSPS_VERSION "$@"
-- 
1.8.1.421.g6236851

^ permalink raw reply related

* [PATCH 3/3] cvsimport: start adding cvsps 3.x support
From: Junio C Hamano @ 2013-01-14  1:40 UTC (permalink / raw)
  To: git; +Cc: jrnieder, mhagger, esr, chris
In-Reply-To: <1358127629-7500-1-git-send-email-gitster@pobox.com>

The new cvsps 3.x series lacks support of some options cvsps 2.x
series had and used by cvsimport-2; add a replacement program from
the author of cvsps 3.x and allow users to choose it by setting the
GIT_CVSPS_VERSION environment variable to 3.  We would later add
support to auto-detect the version of installed cvsps to this code
when the environment variable is not set.

Note that in this step, cvsimport-3 that relies on cvsps 3.x does
not have any test coverage.  As cvsimport-3 supports most of the
command line options cvsimport-2, we should be able to tweak some of
t96xx tests and run them with GIT_CVSPS_VERSION set to both 2 and 3,
but that is a topic of later patches that should come on top.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Makefile           |  18 ++-
 git-cvsimport-3.py | 344 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 git-cvsimport.sh   |   4 +-
 3 files changed, 359 insertions(+), 7 deletions(-)
 create mode 100755 git-cvsimport-3.py

diff --git a/Makefile b/Makefile
index b022db2..060cdc2 100644
--- a/Makefile
+++ b/Makefile
@@ -470,8 +470,9 @@ SCRIPT_PERL += git-relink.perl
 SCRIPT_PERL += git-send-email.perl
 SCRIPT_PERL += git-svn.perl
 
-SCRIPT_PYTHON += git-remote-testpy.py
+SCRIPT_PYTHON += git-cvsimport-3.py
 SCRIPT_PYTHON += git-p4.py
+SCRIPT_PYTHON += git-remote-testpy.py
 
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
 	  $(patsubst %.perl,%,$(SCRIPT_PERL)) \
@@ -575,8 +576,11 @@ endif
 ifndef CVSPS2_PATH
 	CVSPS2_PATH = cvsps
 endif
+ifndef CVSPS3_PATH
+	CVSPS3_PATH = cvsps
+endif
 
-export PERL_PATH PYTHON_PATH CVSPS2_PATH
+export PERL_PATH PYTHON_PATH CVSPS2_PATH CVSPS3_PATH
 
 LIB_FILE = libgit.a
 XDIFF_LIB = xdiff/lib.a
@@ -1515,6 +1519,7 @@ PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
 TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
 CVSPS2_PATH_SQ = $(subst ','\'',$(CVSPS2_PATH))
+CVSPS3_PATH_SQ = $(subst ','\'',$(CVSPS3_PATH))
 DIFF_SQ = $(subst ','\'',$(DIFF))
 
 LIBS = $(GITLIBS) $(EXTLIBS)
@@ -1757,12 +1762,15 @@ ifndef NO_PYTHON
 $(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS
 $(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py
 	$(QUIET_GEN)$(RM) $@ $@+ && \
-	INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \
+	INSTLIBDIR_SQ=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \
 		--no-print-directory prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' \
-		instlibdir` && \
+		instlibdir | \
+		sed -e "s/'/'\''/g"` && \
+	echo "InstLibDir is <$$INSTLIBDIR_SQ>" && \
 	sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
 	    -e 's|\(os\.getenv("GITPYTHONLIB"\)[^)]*)|\1,"@@INSTLIBDIR@@")|' \
-	    -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
+	    -e 's|@@CVSPS3_PATH@@|$(CVSPS3_PATH_SQ)|g' \
+	    -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR_SQ"'|g' \
 	    $@.py >$@+ && \
 	chmod +x $@+ && \
 	mv $@+ $@
diff --git a/git-cvsimport-3.py b/git-cvsimport-3.py
new file mode 100755
index 0000000..57f13b7
--- /dev/null
+++ b/git-cvsimport-3.py
@@ -0,0 +1,344 @@
+#!/usr/bin/env python
+#
+# Import CVS history into git
+#
+# Intended to be a near-workalike of Matthias Urlichs's Perl implementation.
+#
+# By Eric S. Raymond <esr@thyrsus.com>, December 2012
+# May be redistributed under the license of the git project.
+
+import sys
+
+if sys.hexversion < 0x02060000:
+    sys.stderr.write("git cvsimport: requires Python 2.6 or later.\n")
+    sys.exit(1)
+
+import os, getopt, subprocess, tempfile, shutil
+
+DEBUG_COMMANDS = 1
+
+class Fatal(Exception):
+    "Unrecoverable error."
+    def __init__(self, msg):
+        Exception.__init__(self)
+        self.msg = msg
+
+def do_or_die(dcmd, legend=""):
+    "Either execute a command or raise a fatal exception."
+    if legend:
+        legend = " "  + legend
+    if verbose >= DEBUG_COMMANDS:
+        sys.stdout.write("git cvsimport: executing '%s'%s\n" % (dcmd, legend))
+    try:
+        retcode = subprocess.call(dcmd, shell=True)
+        if retcode < 0:
+            raise Fatal("git cvsimport: child was terminated by signal %d." % -retcode)
+        elif retcode != 0:
+            raise Fatal("git cvsimport: child returned %d." % retcode)
+    except (OSError, IOError) as e:
+        raise Fatal("git cvsimport: execution of %s%s failed: %s" % (dcmd, legend, e))
+
+def capture_or_die(dcmd, legend=""):
+    "Either execute a command and capture its output or die."
+    if legend:
+        legend = " "  + legend
+    if verbose >= DEBUG_COMMANDS:
+        sys.stdout.write("git cvsimport: executing '%s'%s\n" % (dcmd, legend))
+    try:
+        return subprocess.check_output(dcmd, shell=True)
+    except subprocess.CalledProcessError as e:
+        if e.returncode < 0:
+            sys.stderr.write("git cvsimport: child was terminated by signal %d." % -e.returncode)
+        elif e.returncode != 0:
+            sys.stderr.write("git cvsimport: child returned %d." % e.returncode)
+        sys.exit(1)
+
+class cvsps:
+    "Method class for cvsps back end."
+
+    cvsps = "@@CVSPS3_PATH@@"
+    def __init__(self):
+        self.opts = ""
+        self.revmap = None
+    def set_repo(self, val):
+        "Set the repository root option."
+        if not val.startswith(":"):
+            if not val.startswith(os.sep):
+                val = os.path.abspath(val)
+            val = ":local:" + val
+        self.opts += " --root '%s'" % val
+    def set_authormap(self, val):
+        "Set the author-map file."
+        self.opts += " -A '%s'" % val
+    def set_fuzz(self, val):
+        "Set the commit-similarity window."
+        self.opts += " -z %s" % val
+    def set_nokeywords(self):
+        "Suppress CVS keyword expansion."
+        self.opts += " -k"
+    def add_opts(self, val):
+        "Add options to the engine command line."
+        self.opts += " " + val
+    def set_exclusion(self, val):
+        "Set a file exclusion regexp."
+        self.opts += " -n -f '%s'" % val
+    def set_after(self, val):
+        "Set a date threshold for incremental import."
+        self.opts += " -d '%s'" % val
+    def set_revmap(self, val):
+        "Set the file to which the engine should dump a reference map."
+        self.revmap = val
+        self.opts += " -R '%s'" % self.revmap
+    def set_module(self, val):
+        "Set the module to query."
+        self.opts += " " + val
+    def command(self):
+        "Emit the command implied by all previous options."
+        return self.cvsps + "--fast-export " + self.opts
+
+class cvs2git:
+    "Method class for cvs2git back end."
+    def __init__(self):
+        self.opts = ""
+        self.modulepath = "."
+    def set_authormap(self, _val):
+        "Set the author-map file."
+        sys.stderr.write("git cvsimport: author maping is not supported with cvs2git.\n")
+        sys.exit(1)
+    def set_repo(self, _val):
+        "Set the repository root option."
+        sys.stderr.write("git cvsimport: cvs2git must run within a repository checkout directory.\n")
+        sys.exit(1)
+    def set_fuzz(self, _val):
+        "Set the commit-similarity window."
+        sys.stderr.write("git cvsimport: fuzz setting is not supported with cvs2git.\n")
+        sys.exit(1)
+    def set_nokeywords(self):
+        "Suppress CVS keyword expansion."
+        self.opts += " --keywords-off"
+    def add_opts(self, val):
+        "Add options to the engine command line."
+        self.opts += " " + val
+    def set_exclusion(self, val):
+        "Set a file exclusion regexp."
+        self.opts += " --exclude='%s'" % val
+    def set_after(self, _val):
+        "Set a date threshold for incremental import."
+        sys.stderr.write("git cvsimport: incremental import is not supported with cvs2git.\n")
+        sys.exit(1)
+    def set_revmap(self, _val):
+        "Set the file to which the engine should dump a reference map."
+        sys.stderr.write("git cvsimport: can't get a reference map from cvs2git.\n")
+        sys.exit(1)
+    def set_module(self, val):
+        "Set the module to query."
+        self.modulepath = " " + val
+    def command(self):
+        "Emit the command implied by all previous options."
+        return "(cvs2git --username=git-cvsimport --quiet --quiet --blobfile={0} --dumpfile={1} {2} {3} && cat {0} {1} && rm {0} {1})".format(tempfile.mkstemp()[1], tempfile.mkstemp()[1], self.opts, self.modulepath)
+
+class filesource:
+    "Method class for file-source back end."
+    def __init__(self, filename):
+        self.filename = filename
+    def __complain(self, legend):
+        sys.stderr.write("git cvsimport: %s with file source.\n" % legend)
+        sys.exit(1)
+    def set_repo(self, _val):
+        "Set the repository root option."
+        self.__complain("repository can't be set")
+    def set_authormap(self, _val):
+        "Set the author-map file."
+        sys.stderr.write("git cvsimport: author mapping is not supported with filesource.\n")
+        sys.exit(1)
+    def set_fuzz(self, _val):
+        "Set the commit-similarity window."
+        self.__complain("fuzz can't be set")
+    def set_nokeywords(self, _val):
+        "Suppress CVS keyword expansion."
+        self.__complain("keyword suppression can't be set")
+    def add_opts(self, _val):
+        "Add options to the engine command line."
+        self.__complain("other options can't be set")
+    def set_exclusion(self, _val):
+        "Set a file exclusion regexp."
+        self.__complain("exclusions can't be set")
+    def set_after(self, _val):
+        "Set a date threshold for incremental import."
+        pass
+    def set_revmap(self, _val):
+        "Set the file to which the engine should dump a reference map."
+        sys.stderr.write("git cvsimport: can't get a reference map from cvs2git.\n")
+        sys.exit(1)
+    def set_module(self, _val):
+        "Set the module to query."
+        self.__complain("module can't be set")
+    def command(self):
+        "Emit the command implied by all previous options."
+        return "cat " + self.filename
+
+if __name__ == '__main__':
+    if sys.hexversion < 0x02060000:
+        sys.stderr.write("git cvsimport: requires Python 2.6 or later.\n")
+        sys.exit(1)
+    (options, arguments) = getopt.getopt(sys.argv[1:], "vbe:d:C:r:o:ikus:p:z:P:S:aL:A:Rh")
+    verbose = 0
+    bare = False
+    root = None
+    outdir = os.getcwd()
+    remotize = False
+    import_only = False
+    underscore_to_dot = False
+    slashsubst = None
+    authormap = None
+    revisionmap = False
+    backend = cvsps()
+    for (opt, val) in options:
+        if opt == '-v':
+            verbose += 1
+        elif opt == '-b':
+            bare = True
+        elif opt == '-e':
+            for cls in (cvsps, cvs2git):
+                if cls.__name__ == val:
+                    backend = cls()
+                    break
+            else:
+                sys.stderr.write("git cvsimport: unknown engine %s.\n" % val)
+                sys.exit(1)
+        elif opt == '-d':
+            backend.set_repo(val)
+        elif opt == '-C':
+            outdir = val
+        elif opt == '-r':
+            remotize = True
+        elif opt == '-o':
+            sys.stderr.write("git cvsimport: -o is no longer supported.\n")
+            sys.exit(1)
+        elif opt == '-i':
+            import_only = True
+        elif opt == '-k':
+            backend.set_nokeywords()
+        elif opt == '-u':
+            underscore_to_dot = True
+        elif opt == '-s':
+            slashsubst = val
+        elif opt == '-p':
+            backend.add_opts(val.replace(",", " "))
+        elif opt == '-z':
+            backend.set_fuzz(val)
+        elif opt == '-P':
+            backend = filesource(val)
+            sys.exit(1)
+        elif opt in ('-m', '-M'):
+            sys.stderr.write("git cvsimport: -m and -M are no longer supported: use reposurgeon instead.\n")
+            sys.exit(1)
+        elif opt == '-S':
+            backend.set_exclusion(val)
+        elif opt == '-a':
+            sys.stderr.write("git cvsimport: -a is no longer supported.\n")
+            sys.exit(1)
+        elif opt == '-L':
+            sys.stderr.write("git cvsimport: -L is no longer supported.\n")
+            sys.exit(1)
+        elif opt == '-A':
+            authormap = os.path.abspath(val)
+        elif opt == '-R':
+            revisionmap = True
+        else:
+            print """\
+git cvsimport [-A <author-conv-file>] [-C <git_repository>] [-b] [-d <CVSROOT>]
+     [-e engine] [-h] [-i] [-k] [-p <options-for-cvsps>] [-P <source-file>]
+     [-r <remote>] [-R] [-s <subst>] [-S <regex>] [-u] [-v] [-z <fuzz>]
+     [<CVS_module>]
+"""
+    def metadata(fn, outdir='.'):
+        if bare:
+            return os.path.join(outdir, fn)
+        else:
+            return os.path.join(outdir, ".git", fn)
+
+    try:
+        if outdir:
+            try:
+                # If the output directory does not exist, create it
+                # and initialize it as a git repository.
+                os.mkdir(outdir)
+                do_or_die("git init --quiet " + outdir)
+            except OSError:
+                # Otherwise, assume user wants incremental import.
+                if not bare and not os.path.exists(os.path.join(outdir, ".git")):
+                    raise Fatal("output directory is not a git repository")
+                threshold = capture_or_die("git log -1 --format=%ct").strip()
+                backend.set_after(threshold)
+        if revisionmap:
+            backend.set_revmap(tempfile.mkstemp()[1])
+            markmap = tempfile.mkstemp()[1]
+        if arguments:
+            backend.set_module(arguments[0])
+        gitopts = ""
+        if bare:
+            gitopts += " --bare"
+        if revisionmap:
+            gitopts += " --export-marks='%s'" % markmap
+        if authormap:
+            shutil.copyfile(authormap, metadata("cvs-authors", outdir))
+        if os.path.exists(metadata("cvs-authors", outdir)):
+            backend.set_authormap(metadata("cvs-authors", outdir))
+        do_or_die("%s | (cd %s >/dev/null; git fast-import --quiet %s)" \
+                  % (backend.command(), outdir, gitopts))
+        os.chdir(outdir)
+        if underscore_to_dot or slashsubst:
+            tagnames = capture_or_die("git tag -l")
+            for tag in tagnames.split():
+                if tag:
+                    changed = tag
+                    if underscore_to_dot:
+                        changed = changed.replace("_", ".")
+                    if slashsubst:
+                        changed = changed.replace(os.sep, slashsubst)
+                    if changed != tag:
+                        do_or_die("git tag -f %s %s >/dev/null" % (tag, changed))
+        if underscore_to_dot or slashsubst or remotize:
+            branchnames = capture_or_die("git branch -l")
+            for branch in branchnames.split():
+                if branch:
+                    # Ugh - fragile dependency on branch -l output format
+                    branch = branch[2:]
+                    changed = branch
+                    if underscore_to_dot:
+                        changed = changed.replace("_", ".")
+                    if slashsubst:
+                        changed = changed.replace(os.sep, slashsubst)
+                    if remotize:
+                        changed = os.path.join("remotes", remotize, branch)
+                    if changed != branch:
+                        do_or_die("branch --m %s %s >/dev/null" % (branch, changed))
+        if revisionmap:
+            refd = {}
+            for line in open(backend.revmap):
+                if line.startswith("#"):
+                    continue
+                (fn, rev, mark) = line.split()
+                refd[(fn, rev)] = mark
+            markd = {}
+            for line in open(markmap):
+                if line.startswith("#"):
+                    continue
+                (mark, hashd) = line.split()
+                markd[mark] = hashd
+            with open(metadata("cvs-revisions"), "a") as wfp:
+                for ((fn, rev), val) in refd.items():
+                    if val in markd:
+                        wfp.write("%s %s %s\n" % (fn, rev, markd[val]))
+            os.remove(markmap)
+            os.remove(backend.revmap)
+        if not import_only and not bare:
+            do_or_die("git checkout -q")
+    except Fatal, err:
+        sys.stderr.write("git_cvsimport: " + err.msg + "\n")
+        sys.exit(1)
+    except KeyboardInterrupt:
+        pass
+
+# end
diff --git a/git-cvsimport.sh b/git-cvsimport.sh
index 71ba11d..52ce112 100755
--- a/git-cvsimport.sh
+++ b/git-cvsimport.sh
@@ -1,5 +1,5 @@
 #!/bin/sh
 
-GIT_CVSPS_VERSION=2
+: ${GIT_CVSPS_VERSION=2}
 
-exec git cvsimport-$GIT_CVSPS_VERSION "$@"
+exec git "cvsimport-$GIT_CVSPS_VERSION" "$@"
-- 
1.8.1.421.g6236851

^ permalink raw reply related

* Re: [PATCH 2/3] cvsimport: introduce a version-switch wrapper
From: Junio C Hamano @ 2013-01-14  1:47 UTC (permalink / raw)
  To: git; +Cc: jrnieder, mhagger, esr, chris
In-Reply-To: <1358127629-7500-3-git-send-email-gitster@pobox.com>

Junio C Hamano <gitster@pobox.com> writes:

> In preparation to have both old and new cvsimport during the
> transition period, rename the cvsimport script to cvsimport-2
> and introduce a small wrapper that we can later change it to
> allow users to run either frontend (with their corresponding
> cvsps backends).
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>  .gitignore           |    1 +
>  Makefile             |    3 +-
>  git-cvsimport-2.perl | 1179 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  git-cvsimport.perl   | 1179 --------------------------------------------------

Sorry, I should have done "format-patch -M" for this one.

^ permalink raw reply

* Re: [PATCH/RFC 4/7] contrib/subtree: Code cleaning and refactoring
From: 郑文辉(Techlive Zheng) @ 2013-01-14  3:30 UTC (permalink / raw)
  To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358041958-1998-5-git-send-email-techlivezheng@gmail.com>

2013/1/13 Techlive Zheng <techlivezheng@gmail.com>:
> Mostly prepare for the later tests refactoring.
>
> Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>

I am personally a SP-indenting guy, I did not aware of Git's indenting
policy until now, so it is bad that I replaced all the lines initially
indent using HT with SP in the test file of this commit. I will submit
another set of patches with the right indenting.

^ permalink raw reply

* [PATCH/RFC v2 0/8] contrib/subtree: Reroll to follow Git's whitespace policy
From: Techlive Zheng @ 2013-01-14  3:52 UTC (permalink / raw)
  To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358041958-1998-1-git-send-email-techlivezheng@gmail.com>

David A. Greene (1):
  contrib/subtree: Remove test number comments

Techlive Zheng (7):
  contrib/subtree: Fix whitespaces
  contrib/subtree: Add vim modeline
  contrib/subtree: Ignore testing directory
  contrib/subtree: Code cleaning and refactoring
  contrib/subtree: Make each test self-contained
  contrib/subtree: Use %B for the split commit message
  contrib/subtree: Handle '--prefix' argument with a slash appended

 contrib/subtree/.gitignore         |    5 +-
 contrib/subtree/git-subtree.sh     |   85 +--
 contrib/subtree/git-subtree.txt    |   55 +-
 contrib/subtree/t/t7900-subtree.sh | 1255 +++++++++++++++++++++++-------------
 4 files changed, 905 insertions(+), 495 deletions(-)

-- 
1.8.1

^ permalink raw reply

* [PATCH/RFC v2 1/8] contrib/subtree: Fix whitespaces
From: Techlive Zheng @ 2013-01-14  3:52 UTC (permalink / raw)
  To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358135541-10349-1-git-send-email-techlivezheng@gmail.com>

Previous code does not fulfill Git's whitespace policy.

Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>
---
 contrib/subtree/git-subtree.sh     |  68 ++++----
 contrib/subtree/git-subtree.txt    |  42 ++---
 contrib/subtree/t/t7900-subtree.sh | 314 ++++++++++++++++++-------------------
 3 files changed, 212 insertions(+), 212 deletions(-)

diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index 920c664..70f86ea 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -5,7 +5,7 @@
 # Copyright (C) 2009 Avery Pennarun <apenwarr@gmail.com>
 #
 if [ $# -eq 0 ]; then
-    set -- -h
+	set -- -h
 fi
 OPTS_SPEC="\
 git subtree add   --prefix=<prefix> <commit>
@@ -110,9 +110,9 @@ if [ -z "$prefix" ]; then
 fi
 
 case "$command" in
-	add) [ -e "$prefix" ] && 
+	add) [ -e "$prefix" ] &&
 		die "prefix '$prefix' already exists." ;;
-	*)   [ -e "$prefix" ] || 
+	*)   [ -e "$prefix" ] ||
 		die "'$prefix' does not exist; use 'git subtree add'" ;;
 esac
 
@@ -181,8 +181,8 @@ cache_set()
 	oldrev="$1"
 	newrev="$2"
 	if [ "$oldrev" != "latest_old" \
-	     -a "$oldrev" != "latest_new" \
-	     -a -e "$cachedir/$oldrev" ]; then
+		-a "$oldrev" != "latest_new" \
+		-a -e "$cachedir/$oldrev" ]; then
 		die "cache for $oldrev already exists!"
 	fi
 	echo "$newrev" >"$cachedir/$oldrev"
@@ -304,7 +304,7 @@ copy_commit()
 		read GIT_COMMITTER_NAME
 		read GIT_COMMITTER_EMAIL
 		read GIT_COMMITTER_DATE
-		export  GIT_AUTHOR_NAME \
+		export GIT_AUTHOR_NAME \
 			GIT_AUTHOR_EMAIL \
 			GIT_AUTHOR_DATE \
 			GIT_COMMITTER_NAME \
@@ -327,7 +327,7 @@ add_msg()
 	fi
 	cat <<-EOF
 		$commit_message
-		
+
 		git-subtree-dir: $dir
 		git-subtree-mainline: $latest_old
 		git-subtree-split: $latest_new
@@ -355,7 +355,7 @@ rejoin_msg()
 	fi
 	cat <<-EOF
 		$commit_message
-		
+
 		git-subtree-dir: $dir
 		git-subtree-mainline: $latest_old
 		git-subtree-split: $latest_new
@@ -368,7 +368,7 @@ squash_msg()
 	oldsub="$2"
 	newsub="$3"
 	newsub_short=$(git rev-parse --short "$newsub")
-	
+
 	if [ -n "$oldsub" ]; then
 		oldsub_short=$(git rev-parse --short "$oldsub")
 		echo "Squashed '$dir/' changes from $oldsub_short..$newsub_short"
@@ -378,7 +378,7 @@ squash_msg()
 	else
 		echo "Squashed '$dir/' content from commit $newsub_short"
 	fi
-	
+
 	echo
 	echo "git-subtree-dir: $dir"
 	echo "git-subtree-split: $newsub"
@@ -427,7 +427,7 @@ new_squash_commit()
 	newsub="$3"
 	tree=$(toptree_for_commit $newsub) || exit $?
 	if [ -n "$old" ]; then
-		squash_msg "$dir" "$oldsub" "$newsub" | 
+		squash_msg "$dir" "$oldsub" "$newsub" |
 			git commit-tree "$tree" -p "$old" || exit $?
 	else
 		squash_msg "$dir" "" "$newsub" |
@@ -455,7 +455,7 @@ copy_or_skip()
 		else
 			nonidentical="$parent"
 		fi
-		
+
 		# sometimes both old parents map to the same newparent;
 		# eliminate duplicates
 		is_new=1
@@ -470,7 +470,7 @@ copy_or_skip()
 			p="$p -p $parent"
 		fi
 	done
-	
+
 	if [ -n "$identical" ]; then
 		echo $identical
 	else
@@ -495,14 +495,14 @@ cmd_add()
 	fi
 
 	ensure_clean
-	
+
 	if [ $# -eq 1 ]; then
 		"cmd_add_commit" "$@"
 	elif [ $# -eq 2 ]; then
 		"cmd_add_repository" "$@"
 	else
-	    say "error: parameters were '$@'"
-	    die "Provide either a refspec or a repository and refspec."
+		say "error: parameters were '$@'"
+		die "Provide either a refspec or a repository and refspec."
 	fi
 }
 
@@ -522,19 +522,19 @@ cmd_add_commit()
 	revs=$(git rev-parse $default --revs-only "$@") || exit $?
 	set -- $revs
 	rev="$1"
-	
+
 	debug "Adding $dir as '$rev'..."
 	git read-tree --prefix="$dir" $rev || exit $?
 	git checkout -- "$dir" || exit $?
 	tree=$(git write-tree) || exit $?
-	
+
 	headrev=$(git rev-parse HEAD) || exit $?
 	if [ -n "$headrev" -a "$headrev" != "$rev" ]; then
 		headp="-p $headrev"
 	else
 		headp=
 	fi
-	
+
 	if [ -n "$squash" ]; then
 		rev=$(new_squash_commit "" "" "$rev") || exit $?
 		commit=$(add_squashed_msg "$rev" "$dir" |
@@ -544,7 +544,7 @@ cmd_add_commit()
 			 git commit-tree $tree $headp -p "$rev") || exit $?
 	fi
 	git reset "$commit" || exit $?
-	
+
 	say "Added dir '$dir'"
 }
 
@@ -552,7 +552,7 @@ cmd_split()
 {
 	debug "Splitting $dir..."
 	cache_setup || exit $?
-	
+
 	if [ -n "$onto" ]; then
 		debug "Reading history for --onto=$onto..."
 		git rev-list $onto |
@@ -563,13 +563,13 @@ cmd_split()
 			cache_set $rev $rev
 		done
 	fi
-	
+
 	if [ -n "$ignore_joins" ]; then
 		unrevs=
 	else
 		unrevs="$(find_existing_splits "$dir" "$revs")"
 	fi
-	
+
 	# We can't restrict rev-list to only $dir here, because some of our
 	# parents have the $dir contents the root, and those won't match.
 	# (and rev-list --follow doesn't seem to solve this)
@@ -591,12 +591,12 @@ cmd_split()
 		debug "  parents: $parents"
 		newparents=$(cache_get $parents)
 		debug "  newparents: $newparents"
-		
+
 		tree=$(subtree_for_commit $rev "$dir")
 		debug "  tree is: $tree"
 
 		check_parents $parents
-		
+
 		# ugly.  is there no better way to tell if this is a subtree
 		# vs. a mainline commit?  Does it matter?
 		if [ -z $tree ]; then
@@ -617,7 +617,7 @@ cmd_split()
 	if [ -z "$latest_new" ]; then
 		die "No new revisions were found"
 	fi
-	
+
 	if [ -n "$rejoin" ]; then
 		debug "Merging split branch into HEAD..."
 		latest_old=$(cache_get latest_old)
@@ -645,13 +645,13 @@ cmd_merge()
 {
 	revs=$(git rev-parse $default --revs-only "$@") || exit $?
 	ensure_clean
-	
+
 	set -- $revs
 	if [ $# -ne 1 ]; then
 		die "You must provide exactly one revision.  Got: '$revs'"
 	fi
 	rev="$1"
-	
+
 	if [ -n "$squash" ]; then
 		first_split="$(find_latest_squash "$dir")"
 		if [ -z "$first_split" ]; then
@@ -697,15 +697,15 @@ cmd_pull()
 cmd_push()
 {
 	if [ $# -ne 2 ]; then
-	    die "You must provide <repository> <refspec>"
+		die "You must provide <repository> <refspec>"
 	fi
 	if [ -e "$dir" ]; then
-	    repository=$1
-	    refspec=$2
-	    echo "git push using: " $repository $refspec
-	    git push $repository $(git subtree split --prefix=$prefix):refs/heads/$refspec
+		repository=$1
+		refspec=$2
+		echo "git push using: " $repository $refspec
+		git push $repository $(git subtree split --prefix=$prefix):refs/heads/$refspec
 	else
-	    die "'$dir' must already exist. Try 'git subtree add'."
+		die "'$dir' must already exist. Try 'git subtree add'."
 	fi
 }
 
diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt
index c5bce41..72be8e4 100644
--- a/contrib/subtree/git-subtree.txt
+++ b/contrib/subtree/git-subtree.txt
@@ -86,15 +86,15 @@ merge::
 	go back in time from v2.5 to v2.4, for example.  If your
 	merge introduces a conflict, you can resolve it in the
 	usual ways.
-	
+
 pull::
 	Exactly like 'merge', but parallels 'git pull' in that
 	it fetches the given commit from the specified remote
 	repository.
-	
+
 push::
 	Does a 'split' (see below) using the <prefix> supplied
-	and then does a 'git push' to push the result to the 
+	and then does a 'git push' to push the result to the
 	repository and refspec. This can be used to push your
 	subtree to different branches of the remote repository.
 
@@ -106,19 +106,19 @@ split::
 	contents of <prefix> at the root of the project instead
 	of in a subdirectory.  Thus, the newly created history
 	is suitable for export as a separate git repository.
-	
+
 	After splitting successfully, a single commit id is
 	printed to stdout.  This corresponds to the HEAD of the
 	newly created tree, which you can manipulate however you
 	want.
-	
+
 	Repeated splits of exactly the same history are
 	guaranteed to be identical (ie. to produce the same
 	commit ids).  Because of this, if you add new commits
 	and then re-split, the new commits will be attached as
 	commits on top of the history you generated last time,
 	so 'git merge' and friends will work as expected.
-	
+
 	Note that if you use '--squash' when you merge, you
 	should usually not just '--rejoin' when you split.
 
@@ -155,26 +155,26 @@ OPTIONS FOR add, merge, push, pull
 	project, produce only a single commit that contains all
 	the differences you want to merge, and then merge that
 	new commit into your project.
-	
+
 	Using this option helps to reduce log clutter. People
 	rarely want to see every change that happened between
 	v1.0 and v1.1 of the library they're using, since none of the
 	interim versions were ever included in their application.
-	
+
 	Using '--squash' also helps avoid problems when the same
 	subproject is included multiple times in the same
 	project, or is removed and then re-added.  In such a
 	case, it doesn't make sense to combine the histories
 	anyway, since it's unclear which part of the history
 	belongs to which subtree.
-	
+
 	Furthermore, with '--squash', you can switch back and
 	forth between different versions of a subtree, rather
 	than strictly forward.  'git subtree merge --squash'
 	always adjusts the subtree to match the exactly
 	specified commit, even if getting to that commit would
 	require undoing some changes that were added earlier.
-	
+
 	Whether or not you use '--squash', changes made in your
 	local repository remain intact and can be later split
 	and send upstream to the subproject.
@@ -190,11 +190,11 @@ OPTIONS FOR split
 	commits with the same commit message, but possibly
 	different content, from the original commits, this can help
 	to differentiate them and avoid confusion.
-	
+
 	Whenever you split, you need to use the same
 	<annotation>, or else you don't have a guarantee that
 	the new re-created history will be identical to the old
-	one.  That will prevent merging from working correctly. 
+	one.  That will prevent merging from working correctly.
 	git subtree tries to make it work anyway, particularly
 	if you use --rejoin, but it may not always be effective.
 
@@ -203,8 +203,8 @@ OPTIONS FOR split
 	This option is only valid for the split command.
 
 	After generating the synthetic history, create a new
-	branch called <branch> that contains the new history. 
-	This is suitable for immediate pushing upstream. 
+	branch called <branch> that contains the new history.
+	This is suitable for immediate pushing upstream.
 	<branch> must not already exist.
 
 --ignore-joins::
@@ -227,7 +227,7 @@ OPTIONS FOR split
 	revision of the subproject's history that was imported
 	into your project, and git subtree will attempt to build
 	its history from there.
-	
+
 	If you used 'git subtree add', you should never need
 	this option.
 
@@ -238,18 +238,18 @@ OPTIONS FOR split
 	history back into your main project.  That way, future
 	splits can search only the part of history that has
 	been added since the most recent --rejoin.
-	
+
 	If your split commits end up merged into the upstream
 	subproject, and then you want to get the latest upstream
 	version, this will allow git's merge algorithm to more
 	intelligently avoid conflicts (since it knows these
 	synthetic commits are already part of the upstream
 	repository).
-	
+
 	Unfortunately, using this option results in 'git log'
 	showing an extra copy of every new commit that was
 	created (the original, and the synthetic one).
-	
+
 	If you do all your merges with '--squash', don't use
 	'--rejoin' when you split, because you don't want the
 	subproject's history to be part of your project anyway.
@@ -284,7 +284,7 @@ First, get your own copy of the git.git repository:
 	$ cd test-git
 
 gitweb (commit 1130ef3) was merged into git as of commit
-0a8f4f0, after which it was no longer maintained separately. 
+0a8f4f0, after which it was no longer maintained separately.
 But imagine it had been maintained separately, and we wanted to
 extract git's changes to gitweb since that time, to share with
 the upstream.  You could do this:
@@ -294,7 +294,7 @@ the upstream.  You could do this:
         	--branch gitweb-latest
         $ gitk gitweb-latest
         $ git push git@github.com:whatever/gitweb.git gitweb-latest:master
-        
+
 (We use '0a8f4f0^..' because that means "all the changes from
 0a8f4f0 to the current version, including 0a8f4f0 itself.")
 
@@ -328,7 +328,7 @@ And fast forward again:
 	$ git subtree merge --prefix=gitweb --squash gitweb-latest
 
 And notice that your change is still intact:
-	
+
 	$ ls -l gitweb/myfile
 
 And you can split it out and look at your changes versus
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index bc2eeb0..b98f7d0 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -23,7 +23,7 @@ check_equal()
 {
 	test_debug 'echo'
 	test_debug "echo \"check a:\" \"{$1}\""
-	test_debug "echo \"      b:\" \"{$2}\""
+	test_debug "echo \"		 b:\" \"{$2}\""
 	if [ "$1" = "$2" ]; then
 		return 0
 	else
@@ -62,7 +62,7 @@ last_commit_message()
 
 # 1
 test_expect_success 'init subproj' '
-        test_create_repo subproj
+		test_create_repo subproj
 '
 
 # To the subproject!
@@ -70,24 +70,24 @@ cd subproj
 
 # 2
 test_expect_success 'add sub1' '
-        create sub1 &&
-        git commit -m "sub1" &&
-        git branch sub1 &&
-        git branch -m master subproj
+		create sub1 &&
+		git commit -m "sub1" &&
+		git branch sub1 &&
+		git branch -m master subproj
 '
 
 # 3
 test_expect_success 'add sub2' '
-        create sub2 &&
-        git commit -m "sub2" &&
-        git branch sub2
+		create sub2 &&
+		git commit -m "sub2" &&
+		git branch sub2
 '
 
 # 4
 test_expect_success 'add sub3' '
-        create sub3 &&
-        git commit -m "sub3" &&
-        git branch sub3
+		create sub3 &&
+		git commit -m "sub3" &&
+		git branch sub3
 '
 
 # Back to mainline
@@ -95,173 +95,173 @@ cd ..
 
 # 5
 test_expect_success 'add main4' '
-        create main4 &&
-        git commit -m "main4" &&
-        git branch -m master mainline &&
-        git branch subdir
+		create main4 &&
+		git commit -m "main4" &&
+		git branch -m master mainline &&
+		git branch subdir
 '
 
 # 6
 test_expect_success 'fetch subproj history' '
-        git fetch ./subproj sub1 &&
-        git branch sub1 FETCH_HEAD
+		git fetch ./subproj sub1 &&
+		git branch sub1 FETCH_HEAD
 '
 
 # 7
 test_expect_success 'no subtree exists in main tree' '
-        test_must_fail git subtree merge --prefix=subdir sub1
+		test_must_fail git subtree merge --prefix=subdir sub1
 '
 
 # 8
 test_expect_success 'no pull from non-existant subtree' '
-        test_must_fail git subtree pull --prefix=subdir ./subproj sub1
+		test_must_fail git subtree pull --prefix=subdir ./subproj sub1
 '
 
 # 9
 test_expect_success 'check if --message works for add' '
-        git subtree add --prefix=subdir --message="Added subproject" sub1 &&
-        check_equal ''"$(last_commit_message)"'' "Added subproject" &&
-        undo
+		git subtree add --prefix=subdir --message="Added subproject" sub1 &&
+		check_equal ''"$(last_commit_message)"'' "Added subproject" &&
+		undo
 '
 
 # 10
 test_expect_success 'check if --message works as -m and --prefix as -P' '
-        git subtree add -P subdir -m "Added subproject using git subtree" sub1 &&
-        check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" &&
-        undo
+		git subtree add -P subdir -m "Added subproject using git subtree" sub1 &&
+		check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" &&
+		undo
 '
 
 # 11
 test_expect_success 'check if --message works with squash too' '
-        git subtree add -P subdir -m "Added subproject with squash" --squash sub1 &&
-        check_equal ''"$(last_commit_message)"'' "Added subproject with squash" &&
-        undo
+		git subtree add -P subdir -m "Added subproject with squash" --squash sub1 &&
+		check_equal ''"$(last_commit_message)"'' "Added subproject with squash" &&
+		undo
 '
 
 # 12
 test_expect_success 'add subproj to mainline' '
-        git subtree add --prefix=subdir/ FETCH_HEAD &&
-        check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
+		git subtree add --prefix=subdir/ FETCH_HEAD &&
+		check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
 '
 
 # 13
 # this shouldn't actually do anything, since FETCH_HEAD is already a parent
 test_expect_success 'merge fetched subproj' '
-        git merge -m "merge -s -ours" -s ours FETCH_HEAD
+		git merge -m "merge -s -ours" -s ours FETCH_HEAD
 '
 
 # 14
 test_expect_success 'add main-sub5' '
-        create subdir/main-sub5 &&
-        git commit -m "main-sub5"
+		create subdir/main-sub5 &&
+		git commit -m "main-sub5"
 '
 
 # 15
 test_expect_success 'add main6' '
-        create main6 &&
-        git commit -m "main6 boring"
+		create main6 &&
+		git commit -m "main6 boring"
 '
 
 # 16
 test_expect_success 'add main-sub7' '
-        create subdir/main-sub7 &&
-        git commit -m "main-sub7"
+		create subdir/main-sub7 &&
+		git commit -m "main-sub7"
 '
 
 # 17
 test_expect_success 'fetch new subproj history' '
-        git fetch ./subproj sub2 &&
-        git branch sub2 FETCH_HEAD
+		git fetch ./subproj sub2 &&
+		git branch sub2 FETCH_HEAD
 '
 
 # 18
 test_expect_success 'check if --message works for merge' '
-        git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 &&
-        check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" &&
-        undo
+		git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 &&
+		check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" &&
+		undo
 '
 
 # 19
 test_expect_success 'check if --message for merge works with squash too' '
-        git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 &&
-        check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" &&
-        undo
+		git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 &&
+		check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" &&
+		undo
 '
 
 # 20
 test_expect_success 'merge new subproj history into subdir' '
-        git subtree merge --prefix=subdir FETCH_HEAD &&
-        git branch pre-split &&
-        check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline"
+		git subtree merge --prefix=subdir FETCH_HEAD &&
+		git branch pre-split &&
+		check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline"
 '
 
 # 21
 test_expect_success 'Check that prefix argument is required for split' '
-        echo "You must provide the --prefix option." > expected &&
-        test_must_fail git subtree split > actual 2>&1 &&
-        test_debug "echo -n expected: " &&
-        test_debug "cat expected" &&
-        test_debug "echo -n actual: " &&
-        test_debug "cat actual" &&
-        test_cmp expected actual &&
-        rm -f expected actual
+		echo "You must provide the --prefix option." > expected &&
+		test_must_fail git subtree split > actual 2>&1 &&
+		test_debug "echo -n expected: " &&
+		test_debug "cat expected" &&
+		test_debug "echo -n actual: " &&
+		test_debug "cat actual" &&
+		test_cmp expected actual &&
+		rm -f expected actual
 '
 
 # 22
 test_expect_success 'Check that the <prefix> exists for a split' '
-        echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected &&
-        test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
-        test_debug "echo -n expected: " &&
-        test_debug "cat expected" &&
-        test_debug "echo -n actual: " &&
-        test_debug "cat actual" &&
-        test_cmp expected actual
-#        rm -f expected actual
+		echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected &&
+		test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
+		test_debug "echo -n expected: " &&
+		test_debug "cat expected" &&
+		test_debug "echo -n actual: " &&
+		test_debug "cat actual" &&
+		test_cmp expected actual
+#		 rm -f expected actual
 '
 
 # 23
 test_expect_success 'check if --message works for split+rejoin' '
-        spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
-        git branch spl1 "$spl1" &&
-        check_equal ''"$(last_commit_message)"'' "Split & rejoin" &&
-        undo
+		spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+		git branch spl1 "$spl1" &&
+		check_equal ''"$(last_commit_message)"'' "Split & rejoin" &&
+		undo
 '
 
 # 24
 test_expect_success 'check split with --branch' '
-        spl1=$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin) &&
-        undo &&
-        git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr1 &&
-        check_equal ''"$(git rev-parse splitbr1)"'' "$spl1"
+		spl1=$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin) &&
+		undo &&
+		git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr1 &&
+		check_equal ''"$(git rev-parse splitbr1)"'' "$spl1"
 '
 
 # 25
 test_expect_success 'check split with --branch for an existing branch' '
-        spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
-        undo &&
-        git branch splitbr2 sub1 &&
-        git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr2 &&
-        check_equal ''"$(git rev-parse splitbr2)"'' "$spl1"
+		spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+		undo &&
+		git branch splitbr2 sub1 &&
+		git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr2 &&
+		check_equal ''"$(git rev-parse splitbr2)"'' "$spl1"
 '
 
 # 26
 test_expect_success 'check split with --branch for an incompatible branch' '
-        test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir
+		test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir
 '
 
 
 # 27
 test_expect_success 'check split+rejoin' '
-        spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
-        undo &&
-        git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --rejoin &&
-        check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'"
+		spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+		undo &&
+		git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --rejoin &&
+		check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'"
 '
 
 # 28
 test_expect_success 'add main-sub8' '
-        create subdir/main-sub8 &&
-        git commit -m "main-sub8"
+		create subdir/main-sub8 &&
+		git commit -m "main-sub8"
 '
 
 # To the subproject!
@@ -269,15 +269,15 @@ cd ./subproj
 
 # 29
 test_expect_success 'merge split into subproj' '
-        git fetch .. spl1 &&
-        git branch spl1 FETCH_HEAD &&
-        git merge FETCH_HEAD
+		git fetch .. spl1 &&
+		git branch spl1 FETCH_HEAD &&
+		git merge FETCH_HEAD
 '
 
 # 30
 test_expect_success 'add sub9' '
-        create sub9 &&
-        git commit -m "sub9"
+		create sub9 &&
+		git commit -m "sub9"
 '
 
 # Back to mainline
@@ -285,20 +285,20 @@ cd ..
 
 # 31
 test_expect_success 'split for sub8' '
-        split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"''
-        git branch split2 "$split2"
+		split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"''
+		git branch split2 "$split2"
 '
 
 # 32
 test_expect_success 'add main-sub10' '
-        create subdir/main-sub10 &&
-        git commit -m "main-sub10"
+		create subdir/main-sub10 &&
+		git commit -m "main-sub10"
 '
 
 # 33
 test_expect_success 'split for sub10' '
-        spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' &&
-        git branch spl3 "$spl3"
+		spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' &&
+		git branch spl3 "$spl3"
 '
 
 # To the subproject!
@@ -306,10 +306,10 @@ cd ./subproj
 
 # 34
 test_expect_success 'merge split into subproj' '
-        git fetch .. spl3 &&
-        git branch spl3 FETCH_HEAD &&
-        git merge FETCH_HEAD &&
-        git branch subproj-merge-spl3
+		git fetch .. spl3 &&
+		git branch spl3 FETCH_HEAD &&
+		git merge FETCH_HEAD &&
+		git branch subproj-merge-spl3
 '
 
 chkm="main4 main6"
@@ -320,14 +320,14 @@ chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl)
 
 # 35
 test_expect_success 'make sure exactly the right set of files ends up in the subproj' '
-        subfiles=''"$(git ls-files | fixnl)"'' &&
-        check_equal "$subfiles" "$chkms $chks"
+		subfiles=''"$(git ls-files | fixnl)"'' &&
+		check_equal "$subfiles" "$chkms $chks"
 '
 
 # 36
 test_expect_success 'make sure the subproj history *only* contains commits that affect the subdir' '
-        allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
-        check_equal "$allchanges" "$chkms $chks"
+		allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
+		check_equal "$allchanges" "$chkms $chks"
 '
 
 # Back to mainline
@@ -335,35 +335,35 @@ cd ..
 
 # 37
 test_expect_success 'pull from subproj' '
-        git fetch ./subproj subproj-merge-spl3 &&
-        git branch subproj-merge-spl3 FETCH_HEAD &&
-        git subtree pull --prefix=subdir ./subproj subproj-merge-spl3
+		git fetch ./subproj subproj-merge-spl3 &&
+		git branch subproj-merge-spl3 FETCH_HEAD &&
+		git subtree pull --prefix=subdir ./subproj subproj-merge-spl3
 '
 
 # 38
 test_expect_success 'make sure exactly the right set of files ends up in the mainline' '
-        mainfiles=''"$(git ls-files | fixnl)"'' &&
-        check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
+		mainfiles=''"$(git ls-files | fixnl)"'' &&
+		check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
 '
 
 # 39
 test_expect_success 'make sure each filename changed exactly once in the entire history' '
-        # main-sub?? and /subdir/main-sub?? both change, because those are the
-        # changes that were split into their own history.  And subdir/sub?? never
-        # change, since they were *only* changed in the subtree branch.
-        allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
-        check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"''
+		# main-sub?? and /subdir/main-sub?? both change, because those are the
+		# changes that were split into their own history.  And subdir/sub?? never
+		# change, since they were *only* changed in the subtree branch.
+		allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
+		check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"''
 '
 
 # 40
 test_expect_success 'make sure the --rejoin commits never make it into subproj' '
-        check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' ""
+		check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' ""
 '
 
 # 41
 test_expect_success 'make sure no "git subtree" tagged commits make it into subproj' '
-        # They are meaningless to subproj since one side of the merge refers to the mainline
-        check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' ""
+		# They are meaningless to subproj since one side of the merge refers to the mainline
+		check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' ""
 '
 
 # prepare second pair of repositories
@@ -372,30 +372,30 @@ cd test2
 
 # 42
 test_expect_success 'init main' '
-        test_create_repo main
+		test_create_repo main
 '
 
 cd main
 
 # 43
 test_expect_success 'add main1' '
-        create main1 &&
-        git commit -m "main1"
+		create main1 &&
+		git commit -m "main1"
 '
 
 cd ..
 
 # 44
 test_expect_success 'init sub' '
-        test_create_repo sub
+		test_create_repo sub
 '
 
 cd sub
 
 # 45
 test_expect_success 'add sub2' '
-        create sub2 &&
-        git commit -m "sub2"
+		create sub2 &&
+		git commit -m "sub2"
 '
 
 cd ../main
@@ -404,37 +404,37 @@ cd ../main
 
 # 46
 test_expect_success 'add sub as subdir in main' '
-        git fetch ../sub master &&
-        git branch sub2 FETCH_HEAD &&
-        git subtree add --prefix subdir sub2
+		git fetch ../sub master &&
+		git branch sub2 FETCH_HEAD &&
+		git subtree add --prefix subdir sub2
 '
 
 cd ../sub
 
 # 47
 test_expect_success 'add sub3' '
-        create sub3 &&
-        git commit -m "sub3"
+		create sub3 &&
+		git commit -m "sub3"
 '
 
 cd ../main
 
 # 48
 test_expect_success 'merge from sub' '
-        git fetch ../sub master &&
-        git branch sub3 FETCH_HEAD &&
-        git subtree merge --prefix subdir sub3
+		git fetch ../sub master &&
+		git branch sub3 FETCH_HEAD &&
+		git subtree merge --prefix subdir sub3
 '
 
 # 49
 test_expect_success 'add main-sub4' '
-        create subdir/main-sub4 &&
-        git commit -m "main-sub4"
+		create subdir/main-sub4 &&
+		git commit -m "main-sub4"
 '
 
 # 50
 test_expect_success 'split for main-sub4 without --onto' '
-        git subtree split --prefix subdir --branch mainsub4
+		git subtree split --prefix subdir --branch mainsub4
 '
 
 # at this point, the new commit parent should be sub3 if it is not,
@@ -444,23 +444,23 @@ test_expect_success 'split for main-sub4 without --onto' '
 
 # 51
 test_expect_success 'check that the commit parent is sub3' '
-        check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"''
+		check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"''
 '
 
 # 52
 test_expect_success 'add main-sub5' '
-        mkdir subdir2 &&
-        create subdir2/main-sub5 &&
-        git commit -m "main-sub5"
+		mkdir subdir2 &&
+		create subdir2/main-sub5 &&
+		git commit -m "main-sub5"
 '
 
 # 53
 test_expect_success 'split for main-sub5 without --onto' '
-        # also test that we still can split out an entirely new subtree
-        # if the parent of the first commit in the tree is not empty,
-        # then the new subtree has accidently been attached to something
-        git subtree split --prefix subdir2 --branch mainsub5 &&
-        check_equal ''"$(git log --pretty=format:%P -1 mainsub5)"'' ""
+		# also test that we still can split out an entirely new subtree
+		# if the parent of the first commit in the tree is not empty,
+		# then the new subtree has accidently been attached to something
+		git subtree split --prefix subdir2 --branch mainsub5 &&
+		check_equal ''"$(git log --pretty=format:%P -1 mainsub5)"'' ""
 '
 
 # make sure no patch changes more than one file.  The original set of commits
@@ -489,20 +489,20 @@ joincommits()
 
 # 54
 test_expect_success 'verify one file change per commit' '
-        x= &&
-        list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' &&
-#        test_debug "echo HERE" &&
-#        test_debug "echo ''"$list"''" &&
-        (git log --pretty=format:'"'commit: %H'"' | joincommits |
-        (       while read commit a b; do
-		        test_debug "echo Verifying commit "''"$commit"''
-		        test_debug "echo a: "''"$a"''
-		        test_debug "echo b: "''"$b"''
-		        check_equal "$b" ""
-		        x=1
-	        done
-	        check_equal "$x" 1
-        ))
+		x= &&
+		list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' &&
+#		 test_debug "echo HERE" &&
+#		 test_debug "echo ''"$list"''" &&
+		(git log --pretty=format:'"'commit: %H'"' | joincommits |
+		(		while read commit a b; do
+				test_debug "echo Verifying commit "''"$commit"''
+				test_debug "echo a: "''"$a"''
+				test_debug "echo b: "''"$b"''
+				check_equal "$b" ""
+				x=1
+			done
+			check_equal "$x" 1
+		))
 '
 
 test_done
-- 
1.8.1

^ permalink raw reply related

* [PATCH/RFC v2 2/8] contrib/subtree: Add vim modeline
From: Techlive Zheng @ 2013-01-14  3:52 UTC (permalink / raw)
  To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358135541-10349-1-git-send-email-techlivezheng@gmail.com>

Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>
---
 contrib/subtree/git-subtree.sh     | 2 ++
 contrib/subtree/t/t7900-subtree.sh | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index 70f86ea..88903c0 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -710,3 +710,5 @@ cmd_push()
 }
 
 "cmd_$command" "$@"
+
+# vim: set ts=4 sw=4 noet
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index b98f7d0..e32d31a 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -506,3 +506,5 @@ test_expect_success 'verify one file change per commit' '
 '
 
 test_done
+
+# vim: set ts=4 sw=4 noet
-- 
1.8.1

^ permalink raw reply related

* [PATCH/RFC v2 3/8] contrib/subtree: Ignore testing directory
From: Techlive Zheng @ 2013-01-14  3:52 UTC (permalink / raw)
  To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358135541-10349-1-git-send-email-techlivezheng@gmail.com>

Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>
---
 contrib/subtree/.gitignore | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/contrib/subtree/.gitignore b/contrib/subtree/.gitignore
index 91360a3..59aeeb4 100644
--- a/contrib/subtree/.gitignore
+++ b/contrib/subtree/.gitignore
@@ -1,6 +1,5 @@
 *~
 git-subtree
-git-subtree.xml
 git-subtree.1
-mainline
-subproj
+git-subtree.xml
+t/trash\ directory.*
-- 
1.8.1

^ permalink raw reply related

* [PATCH/RFC v2 4/8] contrib/subtree: Remove test number comments
From: Techlive Zheng @ 2013-01-14  3:52 UTC (permalink / raw)
  To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358135541-10349-1-git-send-email-techlivezheng@gmail.com>

From: "David A. Greene" <greened@obbligato.org>

Delete the comments indicating test numbers as it causes maintenance
headaches.  t*.sh -i will help us find any broken tests.

Signed-off-by: David A. Greene <greened@obbligato.org>
Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>
---
 contrib/subtree/t/t7900-subtree.sh | 55 --------------------------------------
 1 file changed, 55 deletions(-)

diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index e32d31a..851d00c 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -60,7 +60,6 @@ last_commit_message()
 	git log --pretty=format:%s -1
 }
 
-# 1
 test_expect_success 'init subproj' '
 		test_create_repo subproj
 '
@@ -68,7 +67,6 @@ test_expect_success 'init subproj' '
 # To the subproject!
 cd subproj
 
-# 2
 test_expect_success 'add sub1' '
 		create sub1 &&
 		git commit -m "sub1" &&
@@ -76,14 +74,12 @@ test_expect_success 'add sub1' '
 		git branch -m master subproj
 '
 
-# 3
 test_expect_success 'add sub2' '
 		create sub2 &&
 		git commit -m "sub2" &&
 		git branch sub2
 '
 
-# 4
 test_expect_success 'add sub3' '
 		create sub3 &&
 		git commit -m "sub3" &&
@@ -93,7 +89,6 @@ test_expect_success 'add sub3' '
 # Back to mainline
 cd ..
 
-# 5
 test_expect_success 'add main4' '
 		create main4 &&
 		git commit -m "main4" &&
@@ -101,101 +96,85 @@ test_expect_success 'add main4' '
 		git branch subdir
 '
 
-# 6
 test_expect_success 'fetch subproj history' '
 		git fetch ./subproj sub1 &&
 		git branch sub1 FETCH_HEAD
 '
 
-# 7
 test_expect_success 'no subtree exists in main tree' '
 		test_must_fail git subtree merge --prefix=subdir sub1
 '
 
-# 8
 test_expect_success 'no pull from non-existant subtree' '
 		test_must_fail git subtree pull --prefix=subdir ./subproj sub1
 '
 
-# 9
 test_expect_success 'check if --message works for add' '
 		git subtree add --prefix=subdir --message="Added subproject" sub1 &&
 		check_equal ''"$(last_commit_message)"'' "Added subproject" &&
 		undo
 '
 
-# 10
 test_expect_success 'check if --message works as -m and --prefix as -P' '
 		git subtree add -P subdir -m "Added subproject using git subtree" sub1 &&
 		check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" &&
 		undo
 '
 
-# 11
 test_expect_success 'check if --message works with squash too' '
 		git subtree add -P subdir -m "Added subproject with squash" --squash sub1 &&
 		check_equal ''"$(last_commit_message)"'' "Added subproject with squash" &&
 		undo
 '
 
-# 12
 test_expect_success 'add subproj to mainline' '
 		git subtree add --prefix=subdir/ FETCH_HEAD &&
 		check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
 '
 
-# 13
 # this shouldn't actually do anything, since FETCH_HEAD is already a parent
 test_expect_success 'merge fetched subproj' '
 		git merge -m "merge -s -ours" -s ours FETCH_HEAD
 '
 
-# 14
 test_expect_success 'add main-sub5' '
 		create subdir/main-sub5 &&
 		git commit -m "main-sub5"
 '
 
-# 15
 test_expect_success 'add main6' '
 		create main6 &&
 		git commit -m "main6 boring"
 '
 
-# 16
 test_expect_success 'add main-sub7' '
 		create subdir/main-sub7 &&
 		git commit -m "main-sub7"
 '
 
-# 17
 test_expect_success 'fetch new subproj history' '
 		git fetch ./subproj sub2 &&
 		git branch sub2 FETCH_HEAD
 '
 
-# 18
 test_expect_success 'check if --message works for merge' '
 		git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 &&
 		check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" &&
 		undo
 '
 
-# 19
 test_expect_success 'check if --message for merge works with squash too' '
 		git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 &&
 		check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" &&
 		undo
 '
 
-# 20
 test_expect_success 'merge new subproj history into subdir' '
 		git subtree merge --prefix=subdir FETCH_HEAD &&
 		git branch pre-split &&
 		check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline"
 '
 
-# 21
 test_expect_success 'Check that prefix argument is required for split' '
 		echo "You must provide the --prefix option." > expected &&
 		test_must_fail git subtree split > actual 2>&1 &&
@@ -207,7 +186,6 @@ test_expect_success 'Check that prefix argument is required for split' '
 		rm -f expected actual
 '
 
-# 22
 test_expect_success 'Check that the <prefix> exists for a split' '
 		echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected &&
 		test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
@@ -219,7 +197,6 @@ test_expect_success 'Check that the <prefix> exists for a split' '
 #		 rm -f expected actual
 '
 
-# 23
 test_expect_success 'check if --message works for split+rejoin' '
 		spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
 		git branch spl1 "$spl1" &&
@@ -227,7 +204,6 @@ test_expect_success 'check if --message works for split+rejoin' '
 		undo
 '
 
-# 24
 test_expect_success 'check split with --branch' '
 		spl1=$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin) &&
 		undo &&
@@ -235,7 +211,6 @@ test_expect_success 'check split with --branch' '
 		check_equal ''"$(git rev-parse splitbr1)"'' "$spl1"
 '
 
-# 25
 test_expect_success 'check split with --branch for an existing branch' '
 		spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
 		undo &&
@@ -244,13 +219,10 @@ test_expect_success 'check split with --branch for an existing branch' '
 		check_equal ''"$(git rev-parse splitbr2)"'' "$spl1"
 '
 
-# 26
 test_expect_success 'check split with --branch for an incompatible branch' '
 		test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir
 '
 
-
-# 27
 test_expect_success 'check split+rejoin' '
 		spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
 		undo &&
@@ -258,7 +230,6 @@ test_expect_success 'check split+rejoin' '
 		check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'"
 '
 
-# 28
 test_expect_success 'add main-sub8' '
 		create subdir/main-sub8 &&
 		git commit -m "main-sub8"
@@ -267,14 +238,12 @@ test_expect_success 'add main-sub8' '
 # To the subproject!
 cd ./subproj
 
-# 29
 test_expect_success 'merge split into subproj' '
 		git fetch .. spl1 &&
 		git branch spl1 FETCH_HEAD &&
 		git merge FETCH_HEAD
 '
 
-# 30
 test_expect_success 'add sub9' '
 		create sub9 &&
 		git commit -m "sub9"
@@ -283,19 +252,16 @@ test_expect_success 'add sub9' '
 # Back to mainline
 cd ..
 
-# 31
 test_expect_success 'split for sub8' '
 		split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"''
 		git branch split2 "$split2"
 '
 
-# 32
 test_expect_success 'add main-sub10' '
 		create subdir/main-sub10 &&
 		git commit -m "main-sub10"
 '
 
-# 33
 test_expect_success 'split for sub10' '
 		spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' &&
 		git branch spl3 "$spl3"
@@ -304,7 +270,6 @@ test_expect_success 'split for sub10' '
 # To the subproject!
 cd ./subproj
 
-# 34
 test_expect_success 'merge split into subproj' '
 		git fetch .. spl3 &&
 		git branch spl3 FETCH_HEAD &&
@@ -318,13 +283,11 @@ chkms_sub=$(echo $chkms | multiline | sed 's,^,subdir/,' | fixnl)
 chks="sub1 sub2 sub3 sub9"
 chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl)
 
-# 35
 test_expect_success 'make sure exactly the right set of files ends up in the subproj' '
 		subfiles=''"$(git ls-files | fixnl)"'' &&
 		check_equal "$subfiles" "$chkms $chks"
 '
 
-# 36
 test_expect_success 'make sure the subproj history *only* contains commits that affect the subdir' '
 		allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
 		check_equal "$allchanges" "$chkms $chks"
@@ -333,20 +296,17 @@ test_expect_success 'make sure the subproj history *only* contains commits that
 # Back to mainline
 cd ..
 
-# 37
 test_expect_success 'pull from subproj' '
 		git fetch ./subproj subproj-merge-spl3 &&
 		git branch subproj-merge-spl3 FETCH_HEAD &&
 		git subtree pull --prefix=subdir ./subproj subproj-merge-spl3
 '
 
-# 38
 test_expect_success 'make sure exactly the right set of files ends up in the mainline' '
 		mainfiles=''"$(git ls-files | fixnl)"'' &&
 		check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
 '
 
-# 39
 test_expect_success 'make sure each filename changed exactly once in the entire history' '
 		# main-sub?? and /subdir/main-sub?? both change, because those are the
 		# changes that were split into their own history.  And subdir/sub?? never
@@ -355,12 +315,10 @@ test_expect_success 'make sure each filename changed exactly once in the entire
 		check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"''
 '
 
-# 40
 test_expect_success 'make sure the --rejoin commits never make it into subproj' '
 		check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' ""
 '
 
-# 41
 test_expect_success 'make sure no "git subtree" tagged commits make it into subproj' '
 		# They are meaningless to subproj since one side of the merge refers to the mainline
 		check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' ""
@@ -370,14 +328,12 @@ test_expect_success 'make sure no "git subtree" tagged commits make it into subp
 mkdir test2
 cd test2
 
-# 42
 test_expect_success 'init main' '
 		test_create_repo main
 '
 
 cd main
 
-# 43
 test_expect_success 'add main1' '
 		create main1 &&
 		git commit -m "main1"
@@ -385,14 +341,12 @@ test_expect_success 'add main1' '
 
 cd ..
 
-# 44
 test_expect_success 'init sub' '
 		test_create_repo sub
 '
 
 cd sub
 
-# 45
 test_expect_success 'add sub2' '
 		create sub2 &&
 		git commit -m "sub2"
@@ -402,7 +356,6 @@ cd ../main
 
 # check if split can find proper base without --onto
 
-# 46
 test_expect_success 'add sub as subdir in main' '
 		git fetch ../sub master &&
 		git branch sub2 FETCH_HEAD &&
@@ -411,7 +364,6 @@ test_expect_success 'add sub as subdir in main' '
 
 cd ../sub
 
-# 47
 test_expect_success 'add sub3' '
 		create sub3 &&
 		git commit -m "sub3"
@@ -419,20 +371,17 @@ test_expect_success 'add sub3' '
 
 cd ../main
 
-# 48
 test_expect_success 'merge from sub' '
 		git fetch ../sub master &&
 		git branch sub3 FETCH_HEAD &&
 		git subtree merge --prefix subdir sub3
 '
 
-# 49
 test_expect_success 'add main-sub4' '
 		create subdir/main-sub4 &&
 		git commit -m "main-sub4"
 '
 
-# 50
 test_expect_success 'split for main-sub4 without --onto' '
 		git subtree split --prefix subdir --branch mainsub4
 '
@@ -442,19 +391,16 @@ test_expect_success 'split for main-sub4 without --onto' '
 # have been sub3, but it was not, because its cache was not set to
 # itself)
 
-# 51
 test_expect_success 'check that the commit parent is sub3' '
 		check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"''
 '
 
-# 52
 test_expect_success 'add main-sub5' '
 		mkdir subdir2 &&
 		create subdir2/main-sub5 &&
 		git commit -m "main-sub5"
 '
 
-# 53
 test_expect_success 'split for main-sub5 without --onto' '
 		# also test that we still can split out an entirely new subtree
 		# if the parent of the first commit in the tree is not empty,
@@ -487,7 +433,6 @@ joincommits()
 	echo "$commit $all"
 }
 
-# 54
 test_expect_success 'verify one file change per commit' '
 		x= &&
 		list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' &&
-- 
1.8.1

^ permalink raw reply related

* [PATCH/RFC v2 5/8] contrib/subtree: Code cleaning and refactoring
From: Techlive Zheng @ 2013-01-14  3:52 UTC (permalink / raw)
  To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358135541-10349-1-git-send-email-techlivezheng@gmail.com>

Mostly prepare for the later tests refactoring.

Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>
---
 contrib/subtree/t/t7900-subtree.sh | 251 +++++++++++++++++++------------------
 1 file changed, 130 insertions(+), 121 deletions(-)

diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index 851d00c..69bd41c 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -4,7 +4,7 @@
 #
 test_description='Basic porcelain support for subtrees
 
-This test verifies the basic operation of the merge, pull, add
+This test verifies the basic operation of the add, pull, merge
 and split subcommands of git subtree.
 '
 
@@ -18,19 +18,6 @@ create()
 	git add "$1"
 }
 
-
-check_equal()
-{
-	test_debug 'echo'
-	test_debug "echo \"check a:\" \"{$1}\""
-	test_debug "echo \"		 b:\" \"{$2}\""
-	if [ "$1" = "$2" ]; then
-		return 0
-	else
-		return 1
-	fi
-}
-
 fixnl()
 {
 	t=""
@@ -55,6 +42,42 @@ undo()
 	git reset --hard HEAD~
 }
 
+test_equal()
+{
+	test_debug 'echo'
+	test_debug "echo \"check a:\" \"{$1}\""
+	test_debug "echo \"		 b:\" \"{$2}\""
+	if [ "$1" = "$2" ]; then
+		return 0
+	else
+		return 1
+	fi
+}
+
+# Make sure no patch changes more than one file.
+# The original set of commits changed only one file each.
+# A multi-file change would imply that we pruned commits
+# too aggressively.
+join_commits()
+{
+	commit=
+	all=
+	while read x y; do
+		if [ -z "$x" ]; then
+			continue
+		elif [ "$x" = "commit:" ]; then
+			if [ -n "$commit" ]; then
+				echo "$commit $all"
+				all=
+			fi
+			commit="$y"
+		else
+			all="$all $y"
+		fi
+	done
+	echo "$commit $all"
+}
+
 last_commit_message()
 {
 	git log --pretty=format:%s -1
@@ -93,7 +116,7 @@ test_expect_success 'add main4' '
 		create main4 &&
 		git commit -m "main4" &&
 		git branch -m master mainline &&
-		git branch subdir
+		git branch init
 '
 
 test_expect_success 'fetch subproj history' '
@@ -101,40 +124,43 @@ test_expect_success 'fetch subproj history' '
 		git branch sub1 FETCH_HEAD
 '
 
-test_expect_success 'no subtree exists in main tree' '
-		test_must_fail git subtree merge --prefix=subdir sub1
-'
-
 test_expect_success 'no pull from non-existant subtree' '
 		test_must_fail git subtree pull --prefix=subdir ./subproj sub1
 '
 
-test_expect_success 'check if --message works for add' '
-		git subtree add --prefix=subdir --message="Added subproject" sub1 &&
-		check_equal ''"$(last_commit_message)"'' "Added subproject" &&
+test_expect_success 'no merge from non-existant subtree' '
+		test_must_fail git subtree merge --prefix=subdir FETCH_HEAD
+'
+
+test_expect_success 'add subproj as subtree into subdir/ with --prefix' '
+		git subtree add --prefix=subdir FETCH_HEAD &&
+		test_equal "$(last_commit_message)" "Add '\''subdir/'\'' from commit '\''$(git rev-parse FETCH_HEAD)'\''" &&
 		undo
 '
 
-test_expect_success 'check if --message works as -m and --prefix as -P' '
-		git subtree add -P subdir -m "Added subproject using git subtree" sub1 &&
-		check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" &&
+test_expect_success 'add subproj as subtree into subdir/ with --prefix and --message' '
+		git subtree add --prefix=subdir --message="Added subproject" FETCH_HEAD &&
+		test_equal "$(last_commit_message)" "Added subproject" &&
 		undo
 '
 
-test_expect_success 'check if --message works with squash too' '
-		git subtree add -P subdir -m "Added subproject with squash" --squash sub1 &&
-		check_equal ''"$(last_commit_message)"'' "Added subproject with squash" &&
+test_expect_success 'add subproj as subtree into subdir/ with --prefix as -P and --message as -m' '
+		git subtree add -P subdir -m "Added subproject" FETCH_HEAD &&
+		test_equal "$(last_commit_message)" "Added subproject" &&
 		undo
 '
 
-test_expect_success 'add subproj to mainline' '
-		git subtree add --prefix=subdir/ FETCH_HEAD &&
-		check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
+test_expect_success 'add subproj as subtree into subdir/ with --squash and --prefix and --message' '
+		git subtree add --prefix=subdir --message="Added subproject with squash" --squash FETCH_HEAD &&
+		test_equal "$(last_commit_message)" "Added subproject with squash" &&
+		undo
 '
 
-# this shouldn't actually do anything, since FETCH_HEAD is already a parent
-test_expect_success 'merge fetched subproj' '
-		git merge -m "merge -s -ours" -s ours FETCH_HEAD
+test_expect_success 'merge the added subproj again, should do nothing' '
+		git subtree add --prefix=subdir FETCH_HEAD &&
+		# this shouldn not actually do anything, since FETCH_HEAD
+		# is already a parent
+		git merge -s ours -m "merge -s -ours" FETCH_HEAD
 '
 
 test_expect_success 'add main-sub5' '
@@ -157,25 +183,30 @@ test_expect_success 'fetch new subproj history' '
 		git branch sub2 FETCH_HEAD
 '
 
-test_expect_success 'check if --message works for merge' '
-		git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 &&
-		check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" &&
+test_expect_success 'merge new subproj history into subdir/ with --prefix' '
+		git subtree merge --prefix=subdir FETCH_HEAD &&
+		test_equal "$(last_commit_message)" "Merge commit '\''$(git rev-parse FETCH_HEAD)'\'' into mainline" &&
 		undo
 '
 
-test_expect_success 'check if --message for merge works with squash too' '
-		git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 &&
-		check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" &&
+test_expect_success 'merge new subproj history into subdir/ with --prefix and --message' '
+		git subtree merge --prefix=subdir --message="Merged changes from subproject" FETCH_HEAD &&
+		test_equal "$(last_commit_message)" "Merged changes from subproject" &&
 		undo
 '
 
-test_expect_success 'merge new subproj history into subdir' '
+test_expect_success 'merge new subproj history into subdir/ with --squash and --prefix and --message' '
+		git subtree merge --prefix=subdir --message="Merged changes from subproject using squash" --squash FETCH_HEAD &&
+		test_equal "$(last_commit_message)" "Merged changes from subproject using squash" &&
+		undo
+'
+
+test_expect_success 'merge new subproj history into subdir/' '
 		git subtree merge --prefix=subdir FETCH_HEAD &&
-		git branch pre-split &&
-		check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline"
+		git branch pre-split
 '
 
-test_expect_success 'Check that prefix argument is required for split' '
+test_expect_success 'split requires option --prefix' '
 		echo "You must provide the --prefix option." > expected &&
 		test_must_fail git subtree split > actual 2>&1 &&
 		test_debug "echo -n expected: " &&
@@ -186,48 +217,52 @@ test_expect_success 'Check that prefix argument is required for split' '
 		rm -f expected actual
 '
 
-test_expect_success 'Check that the <prefix> exists for a split' '
-		echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected &&
+test_expect_success 'split requires path given by option --prefix must exist' '
+		echo "'\''non-existent-directory'\'' does not exist; use '\''git subtree add'\''" > expected &&
 		test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
 		test_debug "echo -n expected: " &&
 		test_debug "cat expected" &&
 		test_debug "echo -n actual: " &&
 		test_debug "cat actual" &&
-		test_cmp expected actual
-#		 rm -f expected actual
+		test_cmp expected actual &&
+		rm -f expected actual
 '
 
-test_expect_success 'check if --message works for split+rejoin' '
-		spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+test_expect_success 'split subdir/ with --rejoin' '
+		spl1=$(git subtree split --prefix=subdir --annotate="*") &&
 		git branch spl1 "$spl1" &&
-		check_equal ''"$(last_commit_message)"'' "Split & rejoin" &&
+		git subtree split --prefix=subdir --annotate="*" --rejoin &&
+		test_equal "$(last_commit_message)" "Split '\''subdir/'\'' into commit '\''$spl1'\''" &&
 		undo
 '
 
-test_expect_success 'check split with --branch' '
-		spl1=$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin) &&
+test_expect_success 'split subdir/ with --rejoin and --message' '
+		git subtree split --prefix=subdir --message="Split & rejoin" --annotate="*" --rejoin &&
+		test_equal "$(last_commit_message)" "Split & rejoin" &&
+		undo
+'
+
+test_expect_success 'split subdir/ with --branch' '
+		spl1=$(git subtree split --prefix=subdir --message="Split & rejoin" --annotate="*" --rejoin) &&
 		undo &&
-		git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr1 &&
-		check_equal ''"$(git rev-parse splitbr1)"'' "$spl1"
+		git subtree split --prefix=subdir --annotate="*" --branch splitbr1 &&
+		test_equal "$(git rev-parse splitbr1)" "$spl1"
 '
 
-test_expect_success 'check split with --branch for an existing branch' '
-		spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+test_expect_success 'split subdir/ with --branch for an existing branch' '
+		spl1=$(git subtree split --prefix=subdir --annotate="*" --message="Split & rejoin" --rejoin) &&
 		undo &&
 		git branch splitbr2 sub1 &&
-		git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr2 &&
-		check_equal ''"$(git rev-parse splitbr2)"'' "$spl1"
+		git subtree split --prefix=subdir --annotate="*" --branch splitbr2 &&
+		test_equal "$(git rev-parse splitbr2)" "$spl1"
 '
 
-test_expect_success 'check split with --branch for an incompatible branch' '
-		test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir
+test_expect_success 'split subdir/ with --branch for an incompatible branch' '
+		test_must_fail git subtree split --prefix=subdir --branch init
 '
 
-test_expect_success 'check split+rejoin' '
-		spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
-		undo &&
-		git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --rejoin &&
-		check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'"
+test_expect_success 'split and rejoin' '
+		git subtree split --prefix=subdir --annotate="*" --rejoin
 '
 
 test_expect_success 'add main-sub8' '
@@ -253,8 +288,8 @@ test_expect_success 'add sub9' '
 cd ..
 
 test_expect_success 'split for sub8' '
-		split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"''
-		git branch split2 "$split2"
+		spl2=$(git subtree split --prefix=subdir/ --annotate="*" --rejoin) &&
+		git branch spl2 "$spl2"
 '
 
 test_expect_success 'add main-sub10' '
@@ -263,7 +298,7 @@ test_expect_success 'add main-sub10' '
 '
 
 test_expect_success 'split for sub10' '
-		spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' &&
+		spl3=$(git subtree split --prefix=subdir --annotate="*" --rejoin) &&
 		git branch spl3 "$spl3"
 '
 
@@ -284,13 +319,13 @@ chks="sub1 sub2 sub3 sub9"
 chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl)
 
 test_expect_success 'make sure exactly the right set of files ends up in the subproj' '
-		subfiles=''"$(git ls-files | fixnl)"'' &&
-		check_equal "$subfiles" "$chkms $chks"
+		subfiles=$(git ls-files | fixnl) &&
+		test_equal "$subfiles" "$chkms $chks"
 '
 
-test_expect_success 'make sure the subproj history *only* contains commits that affect the subdir' '
-		allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
-		check_equal "$allchanges" "$chkms $chks"
+test_expect_success 'make sure the subproj *only* contains commits that affect the subdir' '
+		allchanges=$(git log --name-only --pretty=format:"" | sort | fixnl) &&
+		test_equal "$allchanges" "$chkms $chks"
 '
 
 # Back to mainline
@@ -303,25 +338,25 @@ test_expect_success 'pull from subproj' '
 '
 
 test_expect_success 'make sure exactly the right set of files ends up in the mainline' '
-		mainfiles=''"$(git ls-files | fixnl)"'' &&
-		check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
+		mainfiles=$(git ls-files | fixnl) &&
+		test_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
 '
 
 test_expect_success 'make sure each filename changed exactly once in the entire history' '
 		# main-sub?? and /subdir/main-sub?? both change, because those are the
 		# changes that were split into their own history.  And subdir/sub?? never
 		# change, since they were *only* changed in the subtree branch.
-		allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
-		check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"''
+		allchanges=$(git log --name-only --pretty=format:"" | sort | fixnl) &&
+		test_equal "$allchanges" "$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"
 '
 
 test_expect_success 'make sure the --rejoin commits never make it into subproj' '
-		check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' ""
+		test_equal "$(git log --pretty=format:"%s" HEAD^2 | grep -i split)" ""
 '
 
 test_expect_success 'make sure no "git subtree" tagged commits make it into subproj' '
 		# They are meaningless to subproj since one side of the merge refers to the mainline
-		check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' ""
+		test_equal "$(git log --pretty=format:"%s%n%b" HEAD^2 | grep "git-subtree.*:")" ""
 '
 
 # prepare second pair of repositories
@@ -359,7 +394,7 @@ cd ../main
 test_expect_success 'add sub as subdir in main' '
 		git fetch ../sub master &&
 		git branch sub2 FETCH_HEAD &&
-		git subtree add --prefix subdir sub2
+		git subtree add --prefix=subdir sub2
 '
 
 cd ../sub
@@ -374,7 +409,7 @@ cd ../main
 test_expect_success 'merge from sub' '
 		git fetch ../sub master &&
 		git branch sub3 FETCH_HEAD &&
-		git subtree merge --prefix subdir sub3
+		git subtree merge --prefix=subdir sub3
 '
 
 test_expect_success 'add main-sub4' '
@@ -383,7 +418,7 @@ test_expect_success 'add main-sub4' '
 '
 
 test_expect_success 'split for main-sub4 without --onto' '
-		git subtree split --prefix subdir --branch mainsub4
+		git subtree split --prefix=subdir --branch mainsub4
 '
 
 # at this point, the new commit parent should be sub3 if it is not,
@@ -392,7 +427,7 @@ test_expect_success 'split for main-sub4 without --onto' '
 # itself)
 
 test_expect_success 'check that the commit parent is sub3' '
-		check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"''
+		test_equal "$(git log --pretty=format:%P -1 mainsub4)" "$(git rev-parse sub3)"
 '
 
 test_expect_success 'add main-sub5' '
@@ -405,49 +440,23 @@ test_expect_success 'split for main-sub5 without --onto' '
 		# also test that we still can split out an entirely new subtree
 		# if the parent of the first commit in the tree is not empty,
 		# then the new subtree has accidently been attached to something
-		git subtree split --prefix subdir2 --branch mainsub5 &&
-		check_equal ''"$(git log --pretty=format:%P -1 mainsub5)"'' ""
+		git subtree split --prefix=subdir2 --branch mainsub5 &&
+		test_equal "$(git log --pretty=format:%P -1 mainsub5)" ""
 '
 
-# make sure no patch changes more than one file.  The original set of commits
-# changed only one file each.  A multi-file change would imply that we pruned
-# commits too aggressively.
-joincommits()
-{
-	commit=
-	all=
-	while read x y; do
-		#echo "{$x}" >&2
-		if [ -z "$x" ]; then
-			continue
-		elif [ "$x" = "commit:" ]; then
-			if [ -n "$commit" ]; then
-				echo "$commit $all"
-				all=
-			fi
-			commit="$y"
-		else
-			all="$all $y"
-		fi
-	done
-	echo "$commit $all"
-}
-
 test_expect_success 'verify one file change per commit' '
 		x= &&
-		list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' &&
-#		 test_debug "echo HERE" &&
-#		 test_debug "echo ''"$list"''" &&
-		(git log --pretty=format:'"'commit: %H'"' | joincommits |
-		(		while read commit a b; do
-				test_debug "echo Verifying commit "''"$commit"''
-				test_debug "echo a: "''"$a"''
-				test_debug "echo b: "''"$b"''
-				check_equal "$b" ""
+		git log --pretty=format:"commit: %H" | join_commits |
+		(
+			while read commit a b; do
+				test_debug "echo Verifying commit $commit"
+				test_debug "echo a: $a"
+				test_debug "echo b: $b"
+				test_equal "$b" ""
 				x=1
 			done
-			check_equal "$x" 1
-		))
+			test_equal "$x" 1
+		)
 '
 
 test_done
-- 
1.8.1

^ permalink raw reply related

* [PATCH/RFC v2 8/8] contrib/subtree: Handle '--prefix' argument with a slash appended
From: Techlive Zheng @ 2013-01-14  3:52 UTC (permalink / raw)
  To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358135541-10349-1-git-send-email-techlivezheng@gmail.com>

'git subtree merge' will fail if the argument of '--prefix' has a slash
appended.

Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>
---
 contrib/subtree/git-subtree.sh     |  2 +-
 contrib/subtree/t/t7900-subtree.sh | 19 +++++++++++++++++++
 2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index d529a76..40100e5 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -83,7 +83,7 @@ while [ $# -gt 0 ]; do
 		--annotate) annotate="$1"; shift ;;
 		--no-annotate) annotate= ;;
 		-b) branch="$1"; shift ;;
-		-P) prefix="$1"; shift ;;
+		-P) prefix="${1%/}"; shift ;;
 		-m) message="$1"; shift ;;
 		--no-prefix) prefix= ;;
 		--onto) onto="$1"; shift ;;
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index 232ed89..297dac4 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -238,6 +238,25 @@ test_expect_success 'merge new subproj history into subdir/ with --squash and --
 	)
 '
 
+test_expect_success 'merge new subproj history into subdir/ with a slash appended to the argument of --prefix' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir/ FETCH_HEAD
+	) &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree merge --prefix=subdir/ FETCH_HEAD &&
+		test_equal "$(last_commit_message)" "Merge commit '\''$(git rev-parse FETCH_HEAD)'\''"
+	)
+'
+
 #
 # Tests for 'git subtree split'
 #
-- 
1.8.1

^ permalink raw reply related

* [PATCH/RFC v2 7/8] contrib/subtree: Use %B for the split commit message
From: Techlive Zheng @ 2013-01-14  3:52 UTC (permalink / raw)
  To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358135541-10349-1-git-send-email-techlivezheng@gmail.com>

Use %B rather than %s%n%n%b to handle the special case of a commit that
only has a subject line.  We don't want to introduce a newline after the
subject, causing generation of a new hash.

After this commit, the newly split branch might differ from the previous
one. If this is the case, --fallback option could help.

Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>
Signed-off-by: David A. Greene <greened@obbligato.org>
---
 contrib/subtree/git-subtree.sh     | 13 ++++++++++-
 contrib/subtree/git-subtree.txt    | 13 +++++++++++
 contrib/subtree/t/t7900-subtree.sh | 47 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 72 insertions(+), 1 deletion(-)

diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index 88903c0..d529a76 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -25,6 +25,7 @@ b,branch=     create a new branch from the split subtree
 ignore-joins  ignore prior --rejoin commits
 onto=         try connecting new tree to an existing one
 rejoin        merge the new branch back into HEAD
+fallback      fallback to the obsolete commit generating mechanism
  options for 'add', 'merge', 'pull' and 'push'
 squash        merge subtree changes as a single commit
 "
@@ -45,6 +46,7 @@ ignore_joins=
 annotate=
 squash=
 message=
+fallback=
 
 debug()
 {
@@ -92,6 +94,8 @@ while [ $# -gt 0 ]; do
 		--no-ignore-joins) ignore_joins= ;;
 		--squash) squash=1 ;;
 		--no-squash) squash= ;;
+		--fallback) fallback=1 ;;
+		--no-fallback) fallback= ;;
 		--) break ;;
 		*) die "Unexpected option: $opt" ;;
 	esac
@@ -296,7 +300,14 @@ copy_commit()
 	# We're going to set some environment vars here, so
 	# do it in a subshell to get rid of them safely later
 	debug copy_commit "{$1}" "{$2}" "{$3}"
-	git log -1 --pretty=format:'%an%n%ae%n%ad%n%cn%n%ce%n%cd%n%s%n%n%b' "$1" |
+
+	if [ -z "$fallback" ]; then
+		log_format='%an%n%ae%n%ad%n%cn%n%ce%n%cd%n%B'
+	else
+		log_format='%an%n%ae%n%ad%n%cn%n%ce%n%cd%n%s%n%n%b'
+	fi
+
+	git log -1 --pretty=format:"$log_format" "$1" |
 	(
 		read GIT_AUTHOR_NAME
 		read GIT_AUTHOR_EMAIL
diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt
index 72be8e4..55d0575 100644
--- a/contrib/subtree/git-subtree.txt
+++ b/contrib/subtree/git-subtree.txt
@@ -254,6 +254,19 @@ OPTIONS FOR split
 	'--rejoin' when you split, because you don't want the
 	subproject's history to be part of your project anyway.
 
+--fallback::
+	Previously, git subtree would introduce an extra new line for
+	the commits whose commit message contains only one line.
+	This behavior has been correct. Unfortunately, for those whose
+	current split branch contains these kind of commits, git subtree
+	will generate a new split branch which differs from the existing
+	split branch in these commits. It is better to use this new
+	split branch, because its commits stay intact within the mainline.
+
+	Otherwise, the previous fault behavior could still be used with
+	this option. This option is only for a compatible purpose, newly
+	split branch should never use this option.
+
 
 EXAMPLE 1. Add command
 ----------------------
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index ef83f31..232ed89 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -399,6 +399,53 @@ test_expect_success 'split subdir/ with --branch for an incompatible branch' '
 	)
 '
 
+test_expect_success 'make sure commits with one line message stay intact after split' '
+	test_create_repo $test_count &&
+	test_create_repo $test_count/subproj &&
+	test_create_commit $test_count main1 &&
+	test_create_commit $test_count/subproj sub1 &&
+	(
+		cd $test_count &&
+		git fetch ./subproj master &&
+		ori_hash=$(git rev-parse FETCH_HEAD) &&
+		git branch subori FETCH_HEAD &&
+		git filter-branch --index-filter '\''git ls-files -s | sed "s-\t-&subdir/-" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"'\'' subori
+		git merge -m "Merge B project as our subdirectory" subori &&
+		git subtree split --prefix subdir --branch splitbr1 &&
+		new_hash_1=$(git rev-parse splitbr1) &&
+		test_equal "$ori_hash" "$new_hash_1" &&
+		git subtree split --prefix subdir --branch splitbr2 --fallback &&
+		new_hash_2=$(git rev-parse splitbr2) &&
+		test_must_fail test_equal "$ori_hash" "$new_hash_2"
+	)
+'
+
+test_expect_success 'make sure --fallback option works correctly for the existing split branch' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count"/subproj &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count"/subproj sub1 &&
+	(
+		cd $test_count &&
+		git fetch ./subproj master &&
+		ori_hash=$(git rev-parse FETCH_HEAD) &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count" subdir/main-sub1 &&
+	(
+		cd $test_count &&
+		git subtree split --prefix subdir --branch splitbr1 &&
+		git subtree split --prefix subdir --branch splitbr2 --fallback &&
+		test_must_fail test_equal "$(git rev-parse splitbr1)" "$(git rev-parse splitbr2)"
+	) &&
+	test_create_commit "$test_count" subdir/main-sub2 &&
+	(
+		cd $test_count &&
+		test_must_fail git subtree split --prefix subdir --branch splitbr2 &&
+		git subtree split --prefix subdir --branch splitbr2 --fallback
+	)
+'
+
 #
 # Validity checking
 #
-- 
1.8.1

^ permalink raw reply related

* [PATCH/RFC v2 6/8] contrib/subtree: Make each test self-contained
From: Techlive Zheng @ 2013-01-14  3:52 UTC (permalink / raw)
  To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358135541-10349-1-git-send-email-techlivezheng@gmail.com>

Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>
---
 contrib/subtree/t/t7900-subtree.sh | 865 ++++++++++++++++++++++++++-----------
 1 file changed, 614 insertions(+), 251 deletions(-)

diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index 69bd41c..ef83f31 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -12,12 +12,6 @@ export TEST_DIRECTORY=$(pwd)/../../../t
 
 . ../../../t/test-lib.sh
 
-create()
-{
-	echo "$1" >"$1"
-	git add "$1"
-}
-
 fixnl()
 {
 	t=""
@@ -37,11 +31,6 @@ multiline()
 	done
 }
 
-undo()
-{
-	git reset --hard HEAD~
-}
-
 test_equal()
 {
 	test_debug 'echo'
@@ -78,373 +67,746 @@ join_commits()
 	echo "$commit $all"
 }
 
+test_create_commit() (
+	repo=$1
+	commit=$2
+	cd "$repo"
+	mkdir -p "$(dirname "$commit")"
+	echo "$commit" > "$commit"
+	git add "$commit"
+	git commit -m "$commit"
+)
+
 last_commit_message()
 {
 	git log --pretty=format:%s -1
 }
 
-test_expect_success 'init subproj' '
-		test_create_repo subproj
-'
-
-# To the subproject!
-cd subproj
-
-test_expect_success 'add sub1' '
-		create sub1 &&
-		git commit -m "sub1" &&
-		git branch sub1 &&
-		git branch -m master subproj
-'
-
-test_expect_success 'add sub2' '
-		create sub2 &&
-		git commit -m "sub2" &&
-		git branch sub2
-'
-
-test_expect_success 'add sub3' '
-		create sub3 &&
-		git commit -m "sub3" &&
-		git branch sub3
-'
-
-# Back to mainline
-cd ..
-
-test_expect_success 'add main4' '
-		create main4 &&
-		git commit -m "main4" &&
-		git branch -m master mainline &&
-		git branch init
-'
-
-test_expect_success 'fetch subproj history' '
-		git fetch ./subproj sub1 &&
-		git branch sub1 FETCH_HEAD
-'
+#
+# Tests for 'git subtree add'
+#
 
 test_expect_success 'no pull from non-existant subtree' '
-		test_must_fail git subtree pull --prefix=subdir ./subproj sub1
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		test_must_fail git subtree pull --prefix=subdir ./subproj master
+	)
 '
 
 test_expect_success 'no merge from non-existant subtree' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
 		test_must_fail git subtree merge --prefix=subdir FETCH_HEAD
+	)
 '
 
 test_expect_success 'add subproj as subtree into subdir/ with --prefix' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
 		git subtree add --prefix=subdir FETCH_HEAD &&
-		test_equal "$(last_commit_message)" "Add '\''subdir/'\'' from commit '\''$(git rev-parse FETCH_HEAD)'\''" &&
-		undo
+		test_equal "$(last_commit_message)" "Add '\''subdir/'\'' from commit '\''$(git rev-parse FETCH_HEAD)'\''"
+	)
 '
 
 test_expect_success 'add subproj as subtree into subdir/ with --prefix and --message' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
 		git subtree add --prefix=subdir --message="Added subproject" FETCH_HEAD &&
-		test_equal "$(last_commit_message)" "Added subproject" &&
-		undo
+		test_equal "$(last_commit_message)" "Added subproject"
+	)
 '
 
 test_expect_success 'add subproj as subtree into subdir/ with --prefix as -P and --message as -m' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
 		git subtree add -P subdir -m "Added subproject" FETCH_HEAD &&
-		test_equal "$(last_commit_message)" "Added subproject" &&
-		undo
+		test_equal "$(last_commit_message)" "Added subproject"
+	)
 '
 
 test_expect_success 'add subproj as subtree into subdir/ with --squash and --prefix and --message' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
 		git subtree add --prefix=subdir --message="Added subproject with squash" --squash FETCH_HEAD &&
-		test_equal "$(last_commit_message)" "Added subproject with squash" &&
-		undo
+		test_equal "$(last_commit_message)" "Added subproject with squash"
+	)
 '
 
 test_expect_success 'merge the added subproj again, should do nothing' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
 		git subtree add --prefix=subdir FETCH_HEAD &&
 		# this shouldn not actually do anything, since FETCH_HEAD
 		# is already a parent
 		git merge -s ours -m "merge -s -ours" FETCH_HEAD
+	)
 '
 
-test_expect_success 'add main-sub5' '
-		create subdir/main-sub5 &&
-		git commit -m "main-sub5"
-'
-
-test_expect_success 'add main6' '
-		create main6 &&
-		git commit -m "main6 boring"
-'
-
-test_expect_success 'add main-sub7' '
-		create subdir/main-sub7 &&
-		git commit -m "main-sub7"
-'
-
-test_expect_success 'fetch new subproj history' '
-		git fetch ./subproj sub2 &&
-		git branch sub2 FETCH_HEAD
-'
+#
+# Tests for 'git subtree merge'
+#
 
 test_expect_success 'merge new subproj history into subdir/ with --prefix' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
 		git subtree merge --prefix=subdir FETCH_HEAD &&
-		test_equal "$(last_commit_message)" "Merge commit '\''$(git rev-parse FETCH_HEAD)'\'' into mainline" &&
-		undo
+		test_equal "$(last_commit_message)" "Merge commit '\''$(git rev-parse FETCH_HEAD)'\''"
+	)
 '
 
 test_expect_success 'merge new subproj history into subdir/ with --prefix and --message' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
 		git subtree merge --prefix=subdir --message="Merged changes from subproject" FETCH_HEAD &&
-		test_equal "$(last_commit_message)" "Merged changes from subproject" &&
-		undo
+		test_equal "$(last_commit_message)" "Merged changes from subproject"
+	)
 '
 
 test_expect_success 'merge new subproj history into subdir/ with --squash and --prefix and --message' '
+	test_create_repo "$test_count/subproj" &&
+	test_create_repo "$test_count" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
 		git subtree merge --prefix=subdir --message="Merged changes from subproject using squash" --squash FETCH_HEAD &&
-		test_equal "$(last_commit_message)" "Merged changes from subproject using squash" &&
-		undo
+		test_equal "$(last_commit_message)" "Merged changes from subproject using squash"
+	)
 '
 
-test_expect_success 'merge new subproj history into subdir/' '
-		git subtree merge --prefix=subdir FETCH_HEAD &&
-		git branch pre-split
-'
+#
+# Tests for 'git subtree split'
+#
 
 test_expect_success 'split requires option --prefix' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD &&
 		echo "You must provide the --prefix option." > expected &&
 		test_must_fail git subtree split > actual 2>&1 &&
 		test_debug "echo -n expected: " &&
 		test_debug "cat expected" &&
 		test_debug "echo -n actual: " &&
 		test_debug "cat actual" &&
-		test_cmp expected actual &&
-		rm -f expected actual
+		test_cmp expected actual
+	)
 '
 
 test_expect_success 'split requires path given by option --prefix must exist' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD &&
 		echo "'\''non-existent-directory'\'' does not exist; use '\''git subtree add'\''" > expected &&
 		test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
 		test_debug "echo -n expected: " &&
 		test_debug "cat expected" &&
 		test_debug "echo -n actual: " &&
 		test_debug "cat actual" &&
-		test_cmp expected actual &&
-		rm -f expected actual
+		test_cmp expected actual
+	)
 '
 
 test_expect_success 'split subdir/ with --rejoin' '
-		spl1=$(git subtree split --prefix=subdir --annotate="*") &&
-		git branch spl1 "$spl1" &&
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count" subdir/main-sub1 &&
+	test_create_commit "$test_count" main2 &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	test_create_commit "$test_count" subdir/main-sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree merge --prefix=subdir FETCH_HEAD &&
+		split_hash=$(git subtree split --prefix=subdir --annotate="*") &&
 		git subtree split --prefix=subdir --annotate="*" --rejoin &&
-		test_equal "$(last_commit_message)" "Split '\''subdir/'\'' into commit '\''$spl1'\''" &&
-		undo
+		test_equal "$(last_commit_message)" "Split '\''subdir/'\'' into commit '\''$split_hash'\''"
+	)
 '
 
 test_expect_success 'split subdir/ with --rejoin and --message' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count" subdir/main-sub1 &&
+	test_create_commit "$test_count" main2 &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	test_create_commit "$test_count" subdir/main-sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree merge --prefix=subdir FETCH_HEAD &&
 		git subtree split --prefix=subdir --message="Split & rejoin" --annotate="*" --rejoin &&
-		test_equal "$(last_commit_message)" "Split & rejoin" &&
-		undo
+		test_equal "$(last_commit_message)" "Split & rejoin"
+	)
 '
 
 test_expect_success 'split subdir/ with --branch' '
-		spl1=$(git subtree split --prefix=subdir --message="Split & rejoin" --annotate="*" --rejoin) &&
-		undo &&
-		git subtree split --prefix=subdir --annotate="*" --branch splitbr1 &&
-		test_equal "$(git rev-parse splitbr1)" "$spl1"
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count" subdir/main-sub1 &&
+	test_create_commit "$test_count" main2 &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	test_create_commit "$test_count" subdir/main-sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree merge --prefix=subdir FETCH_HEAD &&
+		split_hash=$(git subtree split --prefix=subdir --annotate="*") &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br &&
+		test_equal "$(git rev-parse subproj-br)" "$split_hash"
+	)
 '
 
 test_expect_success 'split subdir/ with --branch for an existing branch' '
-		spl1=$(git subtree split --prefix=subdir --annotate="*" --message="Split & rejoin" --rejoin) &&
-		undo &&
-		git branch splitbr2 sub1 &&
-		git subtree split --prefix=subdir --annotate="*" --branch splitbr2 &&
-		test_equal "$(git rev-parse splitbr2)" "$spl1"
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git branch subproj-br FETCH_HEAD &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count" subdir/main-sub1 &&
+	test_create_commit "$test_count" main2 &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	test_create_commit "$test_count" subdir/main-sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree merge --prefix=subdir FETCH_HEAD &&
+		split_hash=$(git subtree split --prefix=subdir --annotate="*") &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br &&
+		test_equal "$(git rev-parse subproj-br)" "$split_hash"
+	)
 '
 
 test_expect_success 'split subdir/ with --branch for an incompatible branch' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git branch init HEAD &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count" subdir/main-sub1 &&
+	test_create_commit "$test_count" main2 &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	test_create_commit "$test_count" subdir/main-sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree merge --prefix=subdir FETCH_HEAD &&
 		test_must_fail git subtree split --prefix=subdir --branch init
+	)
 '
 
-test_expect_success 'split and rejoin' '
-		git subtree split --prefix=subdir --annotate="*" --rejoin
-'
-
-test_expect_success 'add main-sub8' '
-		create subdir/main-sub8 &&
-		git commit -m "main-sub8"
-'
-
-# To the subproject!
-cd ./subproj
-
-test_expect_success 'merge split into subproj' '
-		git fetch .. spl1 &&
-		git branch spl1 FETCH_HEAD &&
-		git merge FETCH_HEAD
-'
-
-test_expect_success 'add sub9' '
-		create sub9 &&
-		git commit -m "sub9"
-'
-
-# Back to mainline
-cd ..
-
-test_expect_success 'split for sub8' '
-		spl2=$(git subtree split --prefix=subdir/ --annotate="*" --rejoin) &&
-		git branch spl2 "$spl2"
-'
-
-test_expect_success 'add main-sub10' '
-		create subdir/main-sub10 &&
-		git commit -m "main-sub10"
-'
-
-test_expect_success 'split for sub10' '
-		spl3=$(git subtree split --prefix=subdir --annotate="*" --rejoin) &&
-		git branch spl3 "$spl3"
-'
-
-# To the subproject!
-cd ./subproj
-
-test_expect_success 'merge split into subproj' '
-		git fetch .. spl3 &&
-		git branch spl3 FETCH_HEAD &&
-		git merge FETCH_HEAD &&
-		git branch subproj-merge-spl3
-'
-
-chkm="main4 main6"
-chkms="main-sub10 main-sub5 main-sub7 main-sub8"
-chkms_sub=$(echo $chkms | multiline | sed 's,^,subdir/,' | fixnl)
-chks="sub1 sub2 sub3 sub9"
-chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl)
+#
+# Validity checking
+#
 
 test_expect_success 'make sure exactly the right set of files ends up in the subproj' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count" subdir/main-sub1 &&
+	test_create_commit "$test_count" main2 &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	test_create_commit "$test_count" subdir/main-sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree merge --prefix=subdir FETCH_HEAD &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	test_create_commit "$test_count/subproj" sub3 &&
+	test_create_commit "$test_count" subdir/main-sub3 &&
+	(
+		cd "$test_count/subproj" &&
+		git fetch .. subproj-br && git merge FETCH_HEAD
+	) &&
+	test_create_commit "$test_count/subproj" sub4 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	test_create_commit "$test_count" subdir/main-sub4 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	(
+		cd "$test_count/subproj" &&
+		git fetch .. subproj-br && git merge FETCH_HEAD &&
+
+		chks="sub1 sub2 sub3 sub4" &&
+		chks_sub=$(echo $chks | multiline | sed '\''s,^,subdir/,'\'' | fixnl) &&
+		chkms="main-sub1 main-sub2 main-sub3 main-sub4" &&
+		chkms_sub=$(echo $chkms | multiline | sed '\''s,^,subdir/,'\'' | fixnl) &&
+
 		subfiles=$(git ls-files | fixnl) &&
 		test_equal "$subfiles" "$chkms $chks"
+	)
 '
 
 test_expect_success 'make sure the subproj *only* contains commits that affect the subdir' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count" subdir/main-sub1 &&
+	test_create_commit "$test_count" main2 &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	test_create_commit "$test_count" subdir/main-sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree merge --prefix=subdir FETCH_HEAD &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	test_create_commit "$test_count/subproj" sub3 &&
+	test_create_commit "$test_count" subdir/main-sub3 &&
+	(
+		cd "$test_count/subproj" &&
+		git fetch .. subproj-br && git merge FETCH_HEAD
+	) &&
+	test_create_commit "$test_count/subproj" sub4 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	test_create_commit "$test_count" subdir/main-sub4 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	(
+		cd "$test_count/subproj" &&
+		git fetch .. subproj-br && git merge FETCH_HEAD &&
+
+		chks="sub1 sub2 sub3 sub4" &&
+		chks_sub=$(echo $chks | multiline | sed '\''s,^,subdir/,'\'' | fixnl) &&
+		chkms="main-sub1 main-sub2 main-sub3 main-sub4" &&
+		chkms_sub=$(echo $chkms | multiline | sed '\''s,^,subdir/,'\'' | fixnl) &&
+
 		allchanges=$(git log --name-only --pretty=format:"" | sort | fixnl) &&
 		test_equal "$allchanges" "$chkms $chks"
-'
-
-# Back to mainline
-cd ..
-
-test_expect_success 'pull from subproj' '
-		git fetch ./subproj subproj-merge-spl3 &&
-		git branch subproj-merge-spl3 FETCH_HEAD &&
-		git subtree pull --prefix=subdir ./subproj subproj-merge-spl3
+	)
 '
 
 test_expect_success 'make sure exactly the right set of files ends up in the mainline' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count" subdir/main-sub1 &&
+	test_create_commit "$test_count" main2 &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	test_create_commit "$test_count" subdir/main-sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree merge --prefix=subdir FETCH_HEAD &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	test_create_commit "$test_count/subproj" sub3 &&
+	test_create_commit "$test_count" subdir/main-sub3 &&
+	(
+		cd "$test_count/subproj" &&
+		git fetch .. subproj-br && git merge FETCH_HEAD
+	) &&
+	test_create_commit "$test_count/subproj" sub4 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	test_create_commit "$test_count" subdir/main-sub4 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	(
+		cd "$test_count/subproj" &&
+		git fetch .. subproj-br && git merge FETCH_HEAD
+	) &&
+	(
+		cd "$test_count" &&
+		git subtree pull --prefix=subdir ./subproj master &&
+
+		chkm="main1 main2" &&
+		chks="sub1 sub2 sub3 sub4" &&
+		chks_sub=$(echo $chks | multiline | sed '\''s,^,subdir/,'\'' | fixnl) &&
+		chkms="main-sub1 main-sub2 main-sub3 main-sub4" &&
+		chkms_sub=$(echo $chkms | multiline | sed '\''s,^,subdir/,'\'' | fixnl) &&
+
 		mainfiles=$(git ls-files | fixnl) &&
 		test_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
+	)
 '
 
 test_expect_success 'make sure each filename changed exactly once in the entire history' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count" subdir/main-sub1 &&
+	test_create_commit "$test_count" main2 &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	test_create_commit "$test_count" subdir/main-sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree merge --prefix=subdir FETCH_HEAD &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	test_create_commit "$test_count/subproj" sub3 &&
+	test_create_commit "$test_count" subdir/main-sub3 &&
+	(
+		cd "$test_count/subproj" &&
+		git fetch .. subproj-br && git merge FETCH_HEAD
+	) &&
+	test_create_commit "$test_count/subproj" sub4 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	test_create_commit "$test_count" subdir/main-sub4 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	(
+		cd "$test_count/subproj" &&
+		git fetch .. subproj-br && git merge FETCH_HEAD
+	) &&
+	(
+		cd "$test_count" &&
+		git subtree pull --prefix=subdir ./subproj master &&
+
+		chkm="main1 main2" &&
+		chks="sub1 sub2 sub3 sub4" &&
+		chks_sub=$(echo $chks | multiline | sed '\''s,^,subdir/,'\'' | fixnl) &&
+		chkms="main-sub1 main-sub2 main-sub3 main-sub4" &&
+		chkms_sub=$(echo $chkms | multiline | sed '\''s,^,subdir/,'\'' | fixnl) &&
+
 		# main-sub?? and /subdir/main-sub?? both change, because those are the
 		# changes that were split into their own history.  And subdir/sub?? never
 		# change, since they were *only* changed in the subtree branch.
 		allchanges=$(git log --name-only --pretty=format:"" | sort | fixnl) &&
 		test_equal "$allchanges" "$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"
+	)
 '
 
 test_expect_success 'make sure the --rejoin commits never make it into subproj' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count" subdir/main-sub1 &&
+	test_create_commit "$test_count" main2 &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	test_create_commit "$test_count" subdir/main-sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree merge --prefix=subdir FETCH_HEAD &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	test_create_commit "$test_count/subproj" sub3 &&
+	test_create_commit "$test_count" subdir/main-sub3 &&
+	(
+		cd "$test_count/subproj" &&
+		git fetch .. subproj-br && git merge FETCH_HEAD
+	) &&
+	test_create_commit "$test_count/subproj" sub4 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	test_create_commit "$test_count" subdir/main-sub4 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	(
+		cd "$test_count/subproj" &&
+		git fetch .. subproj-br && git merge FETCH_HEAD
+	) &&
+	(
+		cd "$test_count" &&
+		git subtree pull --prefix=subdir ./subproj master &&
+
 		test_equal "$(git log --pretty=format:"%s" HEAD^2 | grep -i split)" ""
+	)
 '
 
 test_expect_success 'make sure no "git subtree" tagged commits make it into subproj' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count" subdir/main-sub1 &&
+	test_create_commit "$test_count" main2 &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	test_create_commit "$test_count" subdir/main-sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree merge --prefix=subdir FETCH_HEAD &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	test_create_commit "$test_count/subproj" sub3 &&
+	test_create_commit "$test_count" subdir/main-sub3 &&
+	(
+		cd "$test_count/subproj" &&
+		git fetch .. subproj-br && git merge FETCH_HEAD
+	) &&
+	test_create_commit "$test_count/subproj" sub4 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	test_create_commit "$test_count" subdir/main-sub4 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir --annotate="*" --branch subproj-br --rejoin
+	) &&
+	(
+		cd "$test_count/subproj" &&
+		git fetch .. subproj-br && git merge FETCH_HEAD
+	) &&
+	(
+		cd "$test_count" &&
+		git subtree pull --prefix=subdir ./subproj master &&
+
 		# They are meaningless to subproj since one side of the merge refers to the mainline
 		test_equal "$(git log --pretty=format:"%s%n%b" HEAD^2 | grep "git-subtree.*:")" ""
+	)
 '
 
-# prepare second pair of repositories
-mkdir test2
-cd test2
-
-test_expect_success 'init main' '
-		test_create_repo main
-'
-
-cd main
-
-test_expect_success 'add main1' '
-		create main1 &&
-		git commit -m "main1"
-'
-
-cd ..
-
-test_expect_success 'init sub' '
-		test_create_repo sub
-'
-
-cd sub
-
-test_expect_success 'add sub2' '
-		create sub2 &&
-		git commit -m "sub2"
-'
-
-cd ../main
-
-# check if split can find proper base without --onto
-
-test_expect_success 'add sub as subdir in main' '
-		git fetch ../sub master &&
-		git branch sub2 FETCH_HEAD &&
-		git subtree add --prefix=subdir sub2
-'
-
-cd ../sub
-
-test_expect_success 'add sub3' '
-		create sub3 &&
-		git commit -m "sub3"
-'
-
-cd ../main
-
-test_expect_success 'merge from sub' '
-		git fetch ../sub master &&
-		git branch sub3 FETCH_HEAD &&
-		git subtree merge --prefix=subdir sub3
-'
-
-test_expect_success 'add main-sub4' '
-		create subdir/main-sub4 &&
-		git commit -m "main-sub4"
-'
-
-test_expect_success 'split for main-sub4 without --onto' '
-		git subtree split --prefix=subdir --branch mainsub4
-'
-
-# at this point, the new commit parent should be sub3 if it is not,
-# something went wrong (the "newparent" of "master~" commit should
-# have been sub3, but it was not, because its cache was not set to
-# itself)
-
-test_expect_success 'check that the commit parent is sub3' '
-		test_equal "$(git log --pretty=format:%P -1 mainsub4)" "$(git rev-parse sub3)"
-'
+#
+# A new set of tests
+#
 
-test_expect_success 'add main-sub5' '
-		mkdir subdir2 &&
-		create subdir2/main-sub5 &&
-		git commit -m "main-sub5"
-'
+test_expect_success 'make sure "git subtree split" find the correct parent' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git branch subproj-ref FETCH_HEAD &&
+		git subtree merge --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count" subdir/main-sub1 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir --branch subproj-br &&
+
+		# at this point, the new commit parent should be subproj-ref, if it is
+		# not, something went wrong (the "newparent" of "master~" commit should
+		# have been sub2, but it was not, because its cache was not set to
+		# itself)
+		test_equal "$(git log --pretty=format:%P -1 subproj-br)" "$(git rev-parse subproj-ref)"
+	)
+'
+
+test_expect_success 'split a new subtree without --onto option' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree add --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree merge --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count" subdir/main-sub1 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir --branch subproj-br
+	) &&
+	test_create_commit "$test_count" subdir2/main-sub2 &&
+	(
+		cd "$test_count" &&
 
-test_expect_success 'split for main-sub5 without --onto' '
 		# also test that we still can split out an entirely new subtree
 		# if the parent of the first commit in the tree is not empty,
 		# then the new subtree has accidently been attached to something
-		git subtree split --prefix=subdir2 --branch mainsub5 &&
-		test_equal "$(git log --pretty=format:%P -1 mainsub5)" ""
+		git subtree split --prefix=subdir2 --branch subproj2-br &&
+		test_equal "$(git log --pretty=format:%P -1 subproj2-br)" ""
+	)
 '
 
 test_expect_success 'verify one file change per commit' '
+	test_create_repo "$test_count" &&
+	test_create_repo "$test_count/subproj" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subproj" sub1 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git branch sub1 FETCH_HEAD &&
+		git subtree add --prefix=subdir sub1
+	) &&
+	test_create_commit "$test_count/subproj" sub2 &&
+	(
+		cd "$test_count" &&
+		git fetch ./subproj master &&
+		git subtree merge --prefix=subdir FETCH_HEAD
+	) &&
+	test_create_commit "$test_count" subdir/main-sub1 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir --branch subproj-br
+	) &&
+	test_create_commit "$test_count" subdir2/main-sub2 &&
+	(
+		cd "$test_count" &&
+		git subtree split --prefix=subdir2 --branch subproj2-br &&
+
 		x= &&
 		git log --pretty=format:"commit: %H" | join_commits |
 		(
@@ -457,6 +819,7 @@ test_expect_success 'verify one file change per commit' '
 			done
 			test_equal "$x" 1
 		)
+	)
 '
 
 test_done
-- 
1.8.1

^ permalink raw reply related

* Re: [PATCH 2/8] git_remote_helpers: fix input when running under Python 3
From: Michael Haggerty @ 2013-01-14  4:48 UTC (permalink / raw)
  To: John Keeping; +Cc: git, Eric S. Raymond, Felipe Contreras, Sverre Rabbelier
In-Reply-To: <20130113161724.GK4574@serenity.lan>

On 01/13/2013 05:17 PM, John Keeping wrote:
> On Sun, Jan 13, 2013 at 04:26:39AM +0100, Michael Haggerty wrote:
>> On 01/12/2013 08:23 PM, John Keeping wrote:
>>> Although 2to3 will fix most issues in Python 2 code to make it run under
>>> Python 3, it does not handle the new strict separation between byte
>>> strings and unicode strings.  There is one instance in
>>> git_remote_helpers where we are caught by this.
>>>
>>> Fix it by explicitly decoding the incoming byte string into a unicode
>>> string.  In this instance, use the locale under which the application is
>>> running.
>>>
>>> Signed-off-by: John Keeping <john@keeping.me.uk>
>>> ---
>>>  git_remote_helpers/git/importer.py | 2 +-
>>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>>
>>> diff --git a/git_remote_helpers/git/importer.py b/git_remote_helpers/git/importer.py
>>> index e28cc8f..6814003 100644
>>> --- a/git_remote_helpers/git/importer.py
>>> +++ b/git_remote_helpers/git/importer.py
>>> @@ -20,7 +20,7 @@ class GitImporter(object):
>>>          """Returns a dictionary with refs.
>>>          """
>>>          args = ["git", "--git-dir=" + gitdir, "for-each-ref", "refs/heads"]
>>> -        lines = check_output(args).strip().split('\n')
>>> +        lines = check_output(args).decode().strip().split('\n')
>>>          refs = {}
>>>          for line in lines:
>>>              value, name = line.split(' ')
>>>
>>
>> Won't this change cause an exception if the branch names are not all
>> valid strings in the current locale's encoding?  I don't see how this
>> assumption is justified (e.g., see git-check-ref-format(1) for the rules
>> governing reference names).
> 
> Yes it will.  The problem is that for Python 3 we need to decode the
> byte string into a unicode string, which means we need to know what
> encoding it is.
> 
> I don't think we can just say "git-for-each-ref will print refs in
> UTF-8" since AFAIK git doesn't care what encoding the refs are in - I
> suspect that's determined by the filesystem which in the end probably
> maps to whatever bytes the shell fed git when the ref was created.
> 
> That's why I chose the current locale in this case.  I'm hoping someone
> here will correct me if we can do better, but I don't see any way of
> avoiding choosing some encoding here if we want to support Python 3
> (which I think we will, even if we don't right now).

I'm not just trying to be a nuisance here; I'm struggling myself to
understand how a program that cares about strings-vs-bytes (e.g., a
Python3 script) should coexist with a program that doesn't (e.g., git
[1]).  I think this will become a big issue if my Python version of the
commit email script ever gets integrated and then made compatible with
Python3.

You claim "for Python 3 we need to decode the byte string into a unicode
string".  I understand that Python 3 strings are Unicode, but why/when
is it necessary to decode data into a Unicode string as opposed to
leaving it as a byte sequence?

In this particular case (from a cursory look over the code) it seems to
me that (1) decoding to Unicode will sometimes fail for data that git
considers valid and (2) there is no obvious reason that the data cannot
be processed as byte sequences.

Michael

[1] And it doesn't just seem that "git doesn't care about Unicode
*yet*".  It seems more likely that "git will adamantly refuse to deal
with Unicode".  For example, Linus is quite clearly in favor of treating
data as byte sequences in most situations:
https://plus.google.com/111049168280159033135/posts/f3fngVm174f

-- 
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/

^ permalink raw reply

* CFURLCopyResourcePropertyForKey error
From: Chase Tran @ 2013-01-14  5:06 UTC (permalink / raw)
  To: git
In-Reply-To: <CAM95X3Zguz1ajaCPuztExS+2dAYBnmndkcis90aa5Mk_9+26ZA@mail.gmail.com>

I just upgraded from Mac's Lion OS to Mac's Mountain Lion OS, and when
I run gitk i get the following error:

2013-01-13 23:16:30.206 Wish[5201:707] CFURLCopyResourcePropertyForKey
failed because it was passed this URL which has no scheme:

The issue seems to have been talked about here
http://stackoverflow.com/questions/11785126/cfurlcopyresourcepropertyforkey-failed-because-it-was-passed-this-url-which-has

I then upgraded git from version 1.7.11.3 to latest 1.8.1, and issue
still persists. Does anyone know how to fix this?

Thanks,
Chase

^ permalink raw reply

* Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs
From: Michael Haggerty @ 2013-01-14  5:12 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jonathan Nieder, Eric S. Raymond, git, Chris Rorvick
In-Reply-To: <7v8v7wiv3a.fsf@alter.siamese.dyndns.org>

On 01/13/2013 11:20 PM, Junio C Hamano wrote:
> After a quick survey of various distros, I think it is very unlikely
> that we will see "distros move on to newer cvsps, leaving cvsimport
> broken" situation. If anything, it is more like "distros decide to
> ignore the new cvsps, until it is made to work with cvsimport" [*1*].

A better predictor of the distros' decisions is probably which other
packages depend on cvsps.  As one data point: on Debian squeeze and on
Ubuntu precise, only two packages depend on cvsps (git-cvs and
bzr-cvsps-import) and one suggests it (chora2, "a code repository
viewing component for horde framework").  So also by this standard they
are unlikely to feel a lot of pressure to update quickly to cvsps3.

> I think it is probably sensible to [...]
> 
> Agreed?

Yes, I agree that what you propose is a good strategy.

Michael

-- 
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/

^ permalink raw reply

* [PATCH 00/14] Remove unused code from imap-send.c
From: Michael Haggerty @ 2013-01-14  5:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty

As discussed before [1], imap-send.c was copied from isync, including
a lot of code that is not used within the git project.  This patch
series rips a bunch of it out.

[1] http://comments.gmane.org/gmane.comp.version-control.git/210355

Michael Haggerty (14):
  imap-send.c: remove msg_data::flags, which was always zero
  imap-send.c: remove struct msg_data
  iamp-send.c: remove unused struct imap_store_conf
  imap-send.c: remove struct store_conf
  imap-send.c: remove struct message
  imap-send.c: remove some unused fields from struct store
  imap-send.c: inline imap_parse_list() in imap_list()
  imap-send.c: remove struct imap argument to parse_imap_list_l()
  imap-send.c: remove namespace fields from struct imap
  imap-send.c: remove unused field imap_store::trashnc
  imap-send.c: simplify logic in lf_to_crlf()
  imap-send.c: use struct imap_store instead of struct store
  imap-send.c: remove unused field imap_store::uidvalidity
  imap-send.c: fold struct store into struct imap_store

 imap-send.c | 286 +++++++++---------------------------------------------------
 1 file changed, 39 insertions(+), 247 deletions(-)

-- 
1.8.0.3

^ permalink raw reply

* [PATCH 02/14] imap-send.c: remove struct msg_data
From: Michael Haggerty @ 2013-01-14  5:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
In-Reply-To: <1358141566-26081-1-git-send-email-mhagger@alum.mit.edu>

Now that its flags member has been deleted, all that is left is a
strbuf.  So use a strbuf directly.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 imap-send.c | 18 +++++++-----------
 1 file changed, 7 insertions(+), 11 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 451d502..a8cb66a 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -68,10 +68,6 @@ struct store {
 	int recent; /* # of recent messages - don't trust this beyond the initial read */
 };
 
-struct msg_data {
-	struct strbuf data;
-};
-
 static const char imap_send_usage[] = "git imap-send < <mbox>";
 
 #undef DRV_OK
@@ -1279,7 +1275,7 @@ static void lf_to_crlf(struct strbuf *msg)
  * Store msg to IMAP.  Also detach and free the data from msg->data,
  * leaving msg->data empty.
  */
-static int imap_store_msg(struct store *gctx, struct msg_data *msg)
+static int imap_store_msg(struct store *gctx, struct strbuf *msg)
 {
 	struct imap_store *ctx = (struct imap_store *)gctx;
 	struct imap *imap = ctx->imap;
@@ -1287,11 +1283,11 @@ static int imap_store_msg(struct store *gctx, struct msg_data *msg)
 	const char *prefix, *box;
 	int ret;
 
-	lf_to_crlf(&msg->data);
+	lf_to_crlf(msg);
 	memset(&cb, 0, sizeof(cb));
 
-	cb.dlen = msg->data.len;
-	cb.data = strbuf_detach(&msg->data, NULL);
+	cb.dlen = msg->len;
+	cb.data = strbuf_detach(msg, NULL);
 
 	box = gctx->name;
 	prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
@@ -1449,7 +1445,7 @@ static int git_imap_config(const char *key, const char *val, void *cb)
 int main(int argc, char **argv)
 {
 	struct strbuf all_msgs = STRBUF_INIT;
-	struct msg_data msg = {STRBUF_INIT};
+	struct strbuf msg = STRBUF_INIT;
 	struct store *ctx = NULL;
 	int ofs = 0;
 	int r;
@@ -1511,10 +1507,10 @@ int main(int argc, char **argv)
 		unsigned percent = n * 100 / total;
 
 		fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total);
-		if (!split_msg(&all_msgs, &msg.data, &ofs))
+		if (!split_msg(&all_msgs, &msg, &ofs))
 			break;
 		if (server.use_html)
-			wrap_in_html(&msg.data);
+			wrap_in_html(&msg);
 		r = imap_store_msg(ctx, &msg);
 		if (r != DRV_OK)
 			break;
-- 
1.8.0.3

^ permalink raw reply related

* [PATCH 04/14] imap-send.c: remove struct store_conf
From: Michael Haggerty @ 2013-01-14  5:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
In-Reply-To: <1358141566-26081-1-git-send-email-mhagger@alum.mit.edu>

It was never used.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 imap-send.c | 11 -----------
 1 file changed, 11 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index d675e70..3177361 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -33,15 +33,6 @@ typedef void *SSL;
 #include <openssl/hmac.h>
 #endif
 
-struct store_conf {
-	char *name;
-	const char *path; /* should this be here? its interpretation is driver-specific */
-	char *map_inbox;
-	char *trash;
-	unsigned max_size; /* off_t is overkill */
-	unsigned trash_remote_new:1, trash_only_new:1;
-};
-
 /* For message->status */
 #define M_RECENT       (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
 #define M_DEAD         (1<<1) /* expunged */
@@ -55,8 +46,6 @@ struct message {
 };
 
 struct store {
-	struct store_conf *conf; /* foreign */
-
 	/* currently open mailbox */
 	const char *name; /* foreign! maybe preset? */
 	char *path; /* own */
-- 
1.8.0.3

^ permalink raw reply related

* [PATCH 06/14] imap-send.c: remove some unused fields from struct store
From: Michael Haggerty @ 2013-01-14  5:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
In-Reply-To: <1358141566-26081-1-git-send-email-mhagger@alum.mit.edu>


Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 imap-send.c | 15 +++------------
 1 file changed, 3 insertions(+), 12 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index a47008b..fe2bfab 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -36,12 +36,7 @@ typedef void *SSL;
 struct store {
 	/* currently open mailbox */
 	const char *name; /* foreign! maybe preset? */
-	char *path; /* own */
 	int uidvalidity;
-	unsigned char opts; /* maybe preset? */
-	/* note that the following do _not_ reflect stats from msgs, but mailbox totals */
-	int count; /* # of messages */
-	int recent; /* # of recent messages - don't trust this beyond the initial read */
 };
 
 static const char imap_send_usage[] = "git imap-send < <mbox>";
@@ -772,13 +767,10 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
 				   !strcmp("NO", arg) || !strcmp("BYE", arg)) {
 				if ((resp = parse_response_code(ctx, NULL, cmd)) != RESP_OK)
 					return resp;
-			} else if (!strcmp("CAPABILITY", arg))
+			} else if (!strcmp("CAPABILITY", arg)) {
 				parse_capability(imap, cmd);
-			else if ((arg1 = next_arg(&cmd))) {
-				if (!strcmp("EXISTS", arg1))
-					ctx->gen.count = atoi(arg);
-				else if (!strcmp("RECENT", arg1))
-					ctx->gen.recent = atoi(arg);
+			} else if ((arg1 = next_arg(&cmd))) {
+				/* unused */
 			} else {
 				fprintf(stderr, "IMAP error: unable to parse untagged response\n");
 				return RESP_BAD;
@@ -1254,7 +1246,6 @@ static int imap_store_msg(struct store *gctx, struct strbuf *msg)
 	imap->caps = imap->rcaps;
 	if (ret != DRV_OK)
 		return ret;
-	gctx->count++;
 
 	return DRV_OK;
 }
-- 
1.8.0.3

^ permalink raw reply related

* [PATCH 05/14] imap-send.c: remove struct message
From: Michael Haggerty @ 2013-01-14  5:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
In-Reply-To: <1358141566-26081-1-git-send-email-mhagger@alum.mit.edu>

It was never used.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 imap-send.c | 26 --------------------------
 1 file changed, 26 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 3177361..a47008b 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -33,23 +33,10 @@ typedef void *SSL;
 #include <openssl/hmac.h>
 #endif
 
-/* For message->status */
-#define M_RECENT       (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
-#define M_DEAD         (1<<1) /* expunged */
-#define M_FLAGS        (1<<2) /* flags fetched */
-
-struct message {
-	struct message *next;
-	size_t size; /* zero implies "not fetched" */
-	int uid;
-	unsigned char flags, status;
-};
-
 struct store {
 	/* currently open mailbox */
 	const char *name; /* foreign! maybe preset? */
 	char *path; /* own */
-	struct message *msgs; /* own */
 	int uidvalidity;
 	unsigned char opts; /* maybe preset? */
 	/* note that the following do _not_ reflect stats from msgs, but mailbox totals */
@@ -74,8 +61,6 @@ static void imap_warn(const char *, ...);
 
 static char *next_arg(char **);
 
-static void free_generic_messages(struct message *);
-
 __attribute__((format (printf, 3, 4)))
 static int nfsnprintf(char *buf, int blen, const char *fmt, ...);
 
@@ -447,16 +432,6 @@ static char *next_arg(char **s)
 	return ret;
 }
 
-static void free_generic_messages(struct message *msgs)
-{
-	struct message *tmsg;
-
-	for (; msgs; msgs = tmsg) {
-		tmsg = msgs->next;
-		free(msgs);
-	}
-}
-
 static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
 {
 	int ret;
@@ -914,7 +889,6 @@ static void imap_close_server(struct imap_store *ictx)
 static void imap_close_store(struct store *ctx)
 {
 	imap_close_server((struct imap_store *)ctx);
-	free_generic_messages(ctx->msgs);
 	free(ctx);
 }
 
-- 
1.8.0.3

^ permalink raw reply related

* [PATCH 08/14] imap-send.c: remove struct imap argument to parse_imap_list_l()
From: Michael Haggerty @ 2013-01-14  5:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
In-Reply-To: <1358141566-26081-1-git-send-email-mhagger@alum.mit.edu>

It was always set to NULL.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 imap-send.c | 39 +++------------------------------------
 1 file changed, 3 insertions(+), 36 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 452e73e..5238c74 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -578,11 +578,10 @@ static void free_list(struct imap_list *list)
 	}
 }
 
-static int parse_imap_list_l(struct imap *imap, char **sp, struct imap_list **curp, int level)
+static int parse_imap_list_l(char **sp, struct imap_list **curp, int level)
 {
 	struct imap_list *cur;
 	char *s = *sp, *p;
-	int n, bytes;
 
 	for (;;) {
 		while (isspace((unsigned char)*s))
@@ -598,39 +597,7 @@ static int parse_imap_list_l(struct imap *imap, char **sp, struct imap_list **cu
 			/* sublist */
 			s++;
 			cur->val = LIST;
-			if (parse_imap_list_l(imap, &s, &cur->child, level + 1))
-				goto bail;
-		} else if (imap && *s == '{') {
-			/* literal */
-			bytes = cur->len = strtol(s + 1, &s, 10);
-			if (*s != '}')
-				goto bail;
-
-			s = cur->val = xmalloc(cur->len);
-
-			/* dump whats left over in the input buffer */
-			n = imap->buf.bytes - imap->buf.offset;
-
-			if (n > bytes)
-				/* the entire message fit in the buffer */
-				n = bytes;
-
-			memcpy(s, imap->buf.buf + imap->buf.offset, n);
-			s += n;
-			bytes -= n;
-
-			/* mark that we used part of the buffer */
-			imap->buf.offset += n;
-
-			/* now read the rest of the message */
-			while (bytes > 0) {
-				if ((n = socket_read(&imap->buf.sock, s, bytes)) <= 0)
-					goto bail;
-				s += n;
-				bytes -= n;
-			}
-
-			if (buffer_gets(&imap->buf, &s))
+			if (parse_imap_list_l(&s, &cur->child, level + 1))
 				goto bail;
 		} else if (*s == '"') {
 			/* quoted string */
@@ -673,7 +640,7 @@ static struct imap_list *parse_list(char **sp)
 {
 	struct imap_list *head;
 
-	if (!parse_imap_list_l(NULL, sp, &head, 0))
+	if (!parse_imap_list_l(sp, &head, 0))
 		return head;
 	free_list(head);
 	return NULL;
-- 
1.8.0.3

^ permalink raw reply related

* [PATCH 10/14] imap-send.c: remove unused field imap_store::trashnc
From: Michael Haggerty @ 2013-01-14  5:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
In-Reply-To: <1358141566-26081-1-git-send-email-mhagger@alum.mit.edu>


Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 imap-send.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 9616e80..70abe9b 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -127,7 +127,6 @@ struct imap_store {
 	int uidvalidity;
 	struct imap *imap;
 	const char *prefix;
-	unsigned /*currentnc:1,*/ trashnc:1;
 };
 
 struct imap_cmd_cb {
@@ -1079,7 +1078,6 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
 	} /* !preauth */
 
 	ctx->prefix = "";
-	ctx->trashnc = 1;
 	return (struct store *)ctx;
 
 bail:
-- 
1.8.0.3

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox