GIT 0.99.9g

Another snapshot, as slow and steady marth towards 1.0 continues.
Major changes include:

 - Jim Radford's RPM split.
 - Fredrik's recursive merge strategy is now default for two heads merge.
 - Yaacov's SVN importer updates.

Signed-off-by: Junio C Hamano <junkio@cox.net>
diff --git a/Documentation/build-docdep.perl b/Documentation/build-docdep.perl
index 6ff35e0..489389c 100755
--- a/Documentation/build-docdep.perl
+++ b/Documentation/build-docdep.perl
@@ -22,15 +22,11 @@
 while ($changed) {
     $changed = 0;
     while (my ($text, $included) = each %include) {
-	print STDERR "Looking at $text...\n";
 	for my $i (keys %$included) {
-	    print STDERR "$text includes $i.\n";
 	    # $text has include::$i; if $i includes $j
 	    # $text indirectly includes $j.
 	    if (exists $include{$i}) {
-		print STDERR "$i includes something.\n";
 		for my $j (keys %{$include{$i}}) {
-		    print STDERR "$text includes $i include $j\n";
 		    if (!exists $include{$text}{$j}) {
 			$include{$text}{$j} = 1;
 			$included{$j} = 1;
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 3984812..95de436 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -8,7 +8,7 @@
 
 SYNOPSIS
 --------
-'git-tag' [-a | -s | -u <key-id>] [-f] [-m <msg>] <name> [<head>]
+'git-tag' [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <name> [<head>]
 
 DESCRIPTION
 -----------
@@ -30,6 +30,8 @@
 committer identity for the current user is used to find the
 GnuPG key for signing.
 
+`-d <tag>` deletes the tag.
+
 
 Author
 ------
diff --git a/Documentation/howto/using-topic-branches.txt b/Documentation/howto/using-topic-branches.txt
index c6c635a..4698abe 100644
--- a/Documentation/howto/using-topic-branches.txt
+++ b/Documentation/howto/using-topic-branches.txt
@@ -9,7 +9,7 @@
 
 -Tony
 
-Last updated w.r.t. GIT 0.99.5
+Last updated w.r.t. GIT 0.99.9f
 
 Linux subsystem maintenance using GIT
 -------------------------------------
@@ -89,8 +89,8 @@
 
 These can be easily kept up to date by merging from the "linus" branch:
 
- $ git checkout test && git resolve test linus "Auto-update from upstream"
- $ git checkout release && git resolve release linus "Auto-update from upstream"
+ $ git checkout test && git merge "Auto-update from upstream" test linus
+ $ git checkout release && git merge "Auto-update from upstream" release linus
 
 Set up so that you can push upstream to your public tree (you need to
 log-in to the remote system and create an empty tree there before the
@@ -128,7 +128,7 @@
 When you are happy with the state of this change, you can pull it into the
 "test" branch in preparation to make it public:
 
- $ git checkout test && git resolve test speed-up-spinlocks "Pull speed-up-spinlock changes"
+ $ git checkout test && git merge "Pull speed-up-spinlock changes" test speed-up-spinlocks
 
 It is unlikely that you would have any conflicts here ... but you might if you
 spent a while on this step and had also pulled new versions from upstream.
@@ -138,7 +138,7 @@
 see the value of keeping each patch (or patch series) in its own branch.  It
 means that the patches can be moved into the "release" tree in any order.
 
- $ git checkout release && git resolve release speed-up-spinlocks "Pull speed-up-spinlock changes"
+ $ git checkout release && git merge "Pull speed-up-spinlock changes" release speed-up-spinlocks
 
 After a while, you will have a number of branches, and despite the
 well chosen names you picked for each of them, you may forget what
@@ -190,7 +190,7 @@
 
 case "$1" in
 test|release)
-	git checkout $1 && git resolve $1 linus "Auto-update from upstream"
+	git checkout $1 && git merge "Auto-update from upstream" $1 linus
 	;;
 linus)
 	before=$(cat .git/refs/heads/linus)
@@ -231,7 +231,7 @@
 		echo $1 already merged into $2 1>&2
 		exit 1
 	fi
-	git checkout $2 && git resolve $2 $1 "Pull $1 into $2 branch"
+	git checkout $2 && git merge "Pull $1 into $2 branch" $2 $1
 	;;
 *)
 	usage
diff --git a/INSTALL b/INSTALL
index bbb13f3..06b11e1 100644
--- a/INSTALL
+++ b/INSTALL
@@ -5,10 +5,13 @@
 will install the git programs in your own ~/bin/ directory.  If you want
 to do a global install, you can do
 
-	make prefix=/usr install
+	$ make prefix=/usr ;# as yourself
+	# make prefix=/usr install ;# as root
 
-(or prefix=/usr/local, of course).  Some day somebody may send me a RPM
-spec file or something, and you can do "make rpm" or whatever.
+(or prefix=/usr/local, of course).  Just like any program suite
+that uses $prefix, the built results have some paths encoded,
+which are derived from $prefix, so "make all; make prefix=/usr
+install" would not work.
 
 Issues of note:
 
diff --git a/Makefile b/Makefile
index bfdaf76..5bd3ded 100644
--- a/Makefile
+++ b/Makefile
@@ -50,7 +50,7 @@
 # Define USE_STDEV below if you want git to care about the underlying device
 # change being considered an inode change from the update-cache perspective.
 
-GIT_VERSION = 0.99.9f
+GIT_VERSION = 0.99.9g
 
 # CFLAGS is for the users to override from the command line.
 
diff --git a/apply.c b/apply.c
index 3e53b34..cf8aa87 100644
--- a/apply.c
+++ b/apply.c
@@ -53,7 +53,7 @@
 struct patch {
 	char *new_name, *old_name, *def_name;
 	unsigned int old_mode, new_mode;
-	int is_rename, is_copy, is_new, is_delete;
+	int is_rename, is_copy, is_new, is_delete, is_binary;
 	int lines_added, lines_deleted;
 	int score;
 	struct fragment *fragments;
@@ -890,8 +890,18 @@
 
 	patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch);
 
-	if (!patchsize && !metadata_changes(patch))
-		die("patch with only garbage at line %d", linenr);
+	if (!patchsize && !metadata_changes(patch)) {
+		static const char binhdr[] = "Binary files ";
+
+		if (sizeof(binhdr) - 1 < size - offset - hdrsize &&
+		    !memcmp(binhdr, buffer + hdrsize, sizeof(binhdr)-1))
+			patch->is_binary = 1;
+
+		if (patch->is_binary && !apply && !check)
+			;
+		else
+			die("patch with only garbage at line %d", linenr);
+	}
 
 	return offset + hdrsize + patchsize;
 }
@@ -949,9 +959,12 @@
 		add = (add * max + max_change / 2) / max_change;
 		del = total - add;
 	}
-	printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
-		len, name, patch->lines_added + patch->lines_deleted,
-		add, pluses, del, minuses);
+	if (patch->is_binary)
+		printf(" %s%-*s |  Bin\n", prefix, len, name);
+	else
+		printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
+		       len, name, patch->lines_added + patch->lines_deleted,
+		       add, pluses, del, minuses);
 	if (qname)
 		free(qname);
 }
diff --git a/debian/changelog b/debian/changelog
index 03a0f79..e556beb 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+git-core (0.99.9g-0) unstable; urgency=low
+
+  * GIT 0.99.9g
+
+ -- Junio C Hamano <junkio@cox.net>  Wed,  9 Nov 2005 21:01:55 -0800
+
 git-core (0.99.9f-0) unstable; urgency=low
 
   * GIT 0.99.9f
diff --git a/git-core.spec.in b/git-core.spec.in
index 5240dd2..26846d0 100644
--- a/git-core.spec.in
+++ b/git-core.spec.in
@@ -19,32 +19,70 @@
 rudimentary tools that can be used as a SCM, but you should look
 elsewhere for tools for ordinary humans layered on top of this.
 
+%package svn
+Summary:        Git tools for importing Subversion repositories.
+Group:          Development/Tools
+Requires:       subversion
+%description svn
+Git tools for importing Subversion repositories.
+
+%package cvs
+Summary:        Git tools for importing CVS repositories.
+Group:          Development/Tools
+Requires:       cvs
+%description cvs
+Git tools for importing CVS repositories.
+
+%package email
+Summary:        Git tools for sending email.
+Group:          Development/Tools
+%description email
+Git tools for sending email.
+
 %prep
 %setup -q
 
 %build
-make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" WITH_OWN_SUBPROCESS_PY=YesPlease \
+make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" WITH_OWN_SUBPROCESS_PY=YesPlease WITH_SEND_EMAIL=1 \
      prefix=%{_prefix} all %{!?_without_docs: doc}
 
 %install
 rm -rf $RPM_BUILD_ROOT
-make %{_smp_mflags} DESTDIR=$RPM_BUILD_ROOT WITH_OWN_SUBPROCESS_PY=YesPlease \
+make %{_smp_mflags} DESTDIR=$RPM_BUILD_ROOT WITH_OWN_SUBPROCESS_PY=YesPlease WITH_SEND_EMAIL=1 \
      prefix=%{_prefix} mandir=%{_mandir} \
      install %{!?_without_docs: install-doc}
 
+(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "svn|cvs|email" | sed -e s@^$RPM_BUILD_ROOT@@)               > bin-man-files
+%if %{!?_without_docs:1}0
+(find $RPM_BUILD_ROOT%{_mandir} -type f | grep -vE "svn|cvs|email" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-files
+%endif
+
 %clean
 rm -rf $RPM_BUILD_ROOT
 
-%files
+%files svn
+%{_bindir}/*svn*
+%{!?_without_docs: %{_mandir}/man1/*svn*.1*}
+
+%files cvs
+%{_bindir}/*cvs*
+%{!?_without_docs: %{_mandir}/man1/*cvs*.1*}
+
+%files email
+%{_bindir}/*email*
+%{!?_without_docs: %{_mandir}/man1/*email*.1*}
+
+%files -f bin-man-files
 %defattr(-,root,root)
-%{_bindir}/*
 %{_datadir}/git-core/
 %doc README COPYING Documentation/*.txt
 %{!?_without_docs: %doc Documentation/*.html }
-%{!?_without_docs: %{_mandir}/man1/*.1*}
-%{!?_without_docs: %{_mandir}/man7/*.7*}
 
 %changelog
+* Tue Sep 27 2005 Jim Radford <radford@blackbean.org>
+- Move programs with non-standard dependencies (svn, cvs, email)
+  into separate packages
+
 * Tue Sep 27 2005 H. Peter Anvin <hpa@zytor.com>
 - parallelize build
 - COPTS -> CFLAGS
diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index 7074b0c..50b041c 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -64,7 +64,7 @@
 	    last;
 	}; # found it
 	die "Did not find $parent in the parents for this commit!";
-s    }
+    }
 } else { # we don't have a parent from the cmdline...
     if (@parents == 1) { # it's safe to get it from the commit
 	$parent = $parents[0];
diff --git a/git-merge-recursive.py b/git-merge-recursive.py
index 9983cd9..90e889c 100755
--- a/git-merge-recursive.py
+++ b/git-merge-recursive.py
@@ -162,13 +162,10 @@
 # Low level file merging, update and removal
 # ------------------------------------------
 
-MERGE_NONE = 0
-MERGE_TRIVIAL = 1
-MERGE_3WAY = 2
 def mergeFile(oPath, oSha, oMode, aPath, aSha, aMode, bPath, bSha, bMode,
               branch1Name, branch2Name):
 
-    merge = MERGE_NONE
+    merge = False
     clean = True
 
     if stat.S_IFMT(aMode) != stat.S_IFMT(bMode):
@@ -181,7 +178,7 @@
             sha = bSha
     else:
         if aSha != oSha and bSha != oSha:
-            merge = MERGE_TRIVIAL
+            merge = True
 
         if aMode == oMode:
             mode = bMode
@@ -211,7 +208,6 @@
             os.unlink(src1)
             os.unlink(src2)
 
-            merge = MERGE_3WAY
             clean = (code == 0)
         else:
             assert(stat.S_ISLNK(aMode) and stat.S_ISLNK(bMode))
@@ -299,6 +295,7 @@
             else:
                 raise
 
+    branch = branch.replace('/', '_')
     newPath = path + '_' + branch
     suffix = 0
     while newPath in currentFileSet or \
@@ -590,7 +587,7 @@
                 if merge or not clean:
                     print 'Renaming', fmtRename(path, ren1.dstName)
 
-                if merge == MERGE_3WAY:
+                if merge:
                     print 'Auto-merging', ren1.dstName
 
                 if not clean:
@@ -668,7 +665,7 @@
                 if merge or not clean:
                     print 'Renaming', fmtRename(ren1.srcName, ren1.dstName)
 
-                if merge == MERGE_3WAY:
+                if merge:
                     print 'Auto-merging', ren1.dstName
 
                 if not clean:
diff --git a/git-merge.sh b/git-merge.sh
index b810fce..7f481e4 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -110,7 +110,14 @@
 	    die "$remote - not something we can merge"
 done
 
-common=$(git-show-branch --merge-base $head "$@")
+case "$#" in
+1)
+	common=$(git-merge-base --all $head "$@")
+	;;
+*)
+	common=$(git-show-branch --merge-base $head "$@")
+	;;
+esac
 echo "$head" >"$GIT_DIR/ORIG_HEAD"
 
 case "$#,$common,$no_commit" in
@@ -162,7 +169,7 @@
 	up_to_date=t
 	for remote
 	do
-		common_one=$(git-merge-base $head $remote)
+		common_one=$(git-merge-base --all $head $remote)
 		if test "$common_one" != "$remote"
 		then
 			up_to_date=f
diff --git a/git-pull.sh b/git-pull.sh
index 2358af6..3b875ad 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -79,10 +79,22 @@
 	exit 0
 	;;
 ?*' '?*)
-	strategy_default_args='-s octopus'
+	var=`git-var -l | sed -ne 's/^pull\.octopus=/-s /p'`
+	if test '' = "$var"
+	then
+		strategy_default_args='-s octopus'
+	else
+		strategy_default_args=$var
+	fi
 	;;
 *)
-	strategy_default_args='-s resolve'
+	var=`git-var -l | sed -ne 's/^pull\.twohead=/-s /p'`
+	if test '' = "$var"
+	then
+		strategy_default_args='-s recursive'
+	else
+		strategy_default_args=$var
+	fi
 	;;
 esac
 
diff --git a/git-svnimport.perl b/git-svnimport.perl
index 45b6a19..cb9afb9 100755
--- a/git-svnimport.perl
+++ b/git-svnimport.perl
@@ -25,7 +25,7 @@
 use SVN::Core;
 use SVN::Ra;
 
-die "Need CVN:Core 1.2.1 or better" if $SVN::Core::VERSION lt "1.2.1";
+die "Need SVN:Core 1.2.1 or better" if $SVN::Core::VERSION lt "1.2.1";
 
 $SIG{'PIPE'}="IGNORE";
 $ENV{'TZ'}="UTC";
@@ -34,7 +34,7 @@
 
 sub usage() {
 	print STDERR <<END;
-Usage: ${\basename $0}     # fetch/update GIT from CVS
+Usage: ${\basename $0}     # fetch/update GIT from SVN
        [-o branch-for-HEAD] [-h] [-v] [-l max_num_changes]
        [-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
        [-d|-D] [-i] [-u] [-s start_chg] [-m] [-M regex] [SVN_URL]
@@ -53,7 +53,6 @@
 
 $opt_o ||= "origin";
 $opt_s ||= 1;
-$opt_l = 100 unless defined $opt_l;
 my $git_tree = $opt_C;
 $git_tree ||= ".";
 
@@ -112,7 +111,9 @@
 		    DIR => File::Spec->tmpdir(), UNLINK => 1);
 
 	print "... $rev $path ...\n" if $opt_v;
-	eval { $self->{'svn'}->get_file($path,$rev,$fh); };
+	my $pool = SVN::Pool->new();
+	eval { $self->{'svn'}->get_file($path,$rev,$fh,$pool); };
+	$pool->clear;
 	if($@) {
 		return undef if $@ =~ /Attempted to get checksum/;
 		die $@;
@@ -258,10 +259,17 @@
 
 open BRANCHES,">>", "$git_dir/svn2git";
 
-sub get_file($$$) {
-	my($rev,$branch,$path) = @_;
+sub node_kind($$$) {
+	my ($branch, $path, $revision) = @_;
+	my $pool=SVN::Pool->new;
+	my $kind = $svn->{'svn'}->check_path(revert_split_path($branch,$path),$revision,$pool);
+	$pool->clear;
+	return $kind;
+}
 
-	# revert split_path(), below
+sub revert_split_path($$) {
+	my($branch,$path) = @_;
+
 	my $svnpath;
 	$path = "" if $path eq "/"; # this should not happen, but ...
 	if($branch eq "/") {
@@ -272,6 +280,14 @@
 		$svnpath = "$branch_name/$branch/$path";
 	}
 
+	return $svnpath
+}
+
+sub get_file($$$) {
+	my($rev,$branch,$path) = @_;
+
+	my $svnpath = revert_split_path($branch,$path);
+
 	# now get it
 	my $name;
 	if($opt_d) {
@@ -319,28 +335,57 @@
 	} elsif($path =~ s#^/\Q$branch_name\E/([^/]+)/?##) {
 		$branch = $1;
 	} else {
-		print STDERR "$rev: Unrecognized path: $path\n";
+		my %no_error = (
+			"/" => 1,
+			"/$tag_name" => 1,
+			"/$branch_name" => 1
+		);
+		print STDERR "$rev: Unrecognized path: $path\n" unless (defined $no_error{$path});
 		return ()
 	}
 	$path = "/" if $path eq "";
 	return ($branch,$path);
 }
 
-sub copy_subdir($$$$$$) {
+sub branch_rev($$) {
+
+	my ($srcbranch,$uptorev) = @_;
+
+	my $bbranches = $branches{$srcbranch};
+	my @revs = reverse sort { ($a eq 'LAST' ? 0 : $a) <=> ($b eq 'LAST' ? 0 : $b) } keys %$bbranches;
+	my $therev;
+	foreach my $arev(@revs) {
+		next if  ($arev eq 'LAST');
+		if ($arev <= $uptorev) {
+			$therev = $arev;
+			last;
+		}
+	}
+	return $therev;
+}
+
+sub copy_path($$$$$$$$) {
 	# Somebody copied a whole subdirectory.
 	# We need to find the index entries from the old version which the
 	# SVN log entry points to, and add them to the new place.
 
-	my($newrev,$newbranch,$path,$oldpath,$rev,$new) = @_;
-	my($branch,$srcpath) = split_path($rev,$oldpath);
+	my($newrev,$newbranch,$path,$oldpath,$rev,$node_kind,$new,$parents) = @_;
 
-	my $gitrev = $branches{$branch}{$rev};
+	my($srcbranch,$srcpath) = split_path($rev,$oldpath);
+	my $therev = branch_rev($srcbranch, $rev);
+	my $gitrev = $branches{$srcbranch}{$therev};
 	unless($gitrev) {
 		print STDERR "$newrev:$newbranch: could not find $oldpath \@ $rev\n";
 		return;
 	}
-	print "$newrev:$newbranch:$path: copying from $branch:$srcpath @ $rev\n" if $opt_v;
-	$srcpath =~ s#/*$#/#;
+	if ($srcbranch ne $newbranch) {
+		push(@$parents, $branches{$srcbranch}{'LAST'});
+	}
+	print "$newrev:$newbranch:$path: copying from $srcbranch:$srcpath @ $rev\n" if $opt_v;
+	if ($node_kind eq $SVN::Node::dir) {
+			$srcpath =~ s#/*$#/#;
+	}
+	
 	open my $f,"-|","git-ls-tree","-r","-z",$gitrev,$srcpath;
 	local $/ = "\0";
 	while(<$f>) {
@@ -348,9 +393,12 @@
 		my($m,$p) = split(/\t/,$_,2);
 		my($mode,$type,$sha1) = split(/ /,$m);
 		next if $type ne "blob";
-		$p = substr($p,length($srcpath)-1);
-		print "... found $path$p ...\n" if $opt_v;
-		push(@$new,[$mode,$sha1,$path.$p]);
+		if ($node_kind eq $SVN::Node::dir) {
+			$p = $path . substr($p,length($srcpath)-1);
+		} else {
+			$p = $path;
+		}
+		push(@$new,[$mode,$sha1,$p]);	
 	}
 	close($f) or
 		print STDERR "$newrev:$newbranch: could not list files in $oldpath \@ $rev\n";
@@ -359,7 +407,7 @@
 sub commit {
 	my($branch, $changed_paths, $revision, $author, $date, $message) = @_;
 	my($author_name,$author_email,$dest);
-	my(@old,@new);
+	my(@old,@new,@parents);
 
 	if (not defined $author) {
 		$author_name = $author_email = "unknown";
@@ -446,6 +494,8 @@
 		$last_rev = $rev;
 	}
 
+	push (@parents, $rev) if defined $rev;
+
 	my $cid;
 	if($tag and not %$changed_paths) {
 		$cid = $rev;
@@ -454,39 +504,31 @@
 		foreach my $path(@paths) {
 			my $action = $changed_paths->{$path};
 
-			if ($action->[0] eq "A") {
-				my $f = get_file($revision,$branch,$path);
-				if($f) {
-					push(@new,$f) if $f;
-				} elsif($action->[1]) {
-					copy_subdir($revision,$branch,$path,$action->[1],$action->[2],\@new);
-				} else {
-					my $opath = $action->[3];
-					print STDERR "$revision: $branch: could not fetch '$opath'\n";
+			if ($action->[0] eq "R") {
+				# refer to a file/tree in an earlier commit
+				push(@old,$path); # remove any old stuff
+			}
+			if(($action->[0] eq "A") || ($action->[0] eq "R")) {
+				my $node_kind = node_kind($branch,$path,$revision);
+				if($action->[1]) {
+					copy_path($revision,$branch,$path,$action->[1],$action->[2],$node_kind,\@new,\@parents);
+				} elsif ($node_kind eq $SVN::Node::file) {
+					my $f = get_file($revision,$branch,$path);
+					if ($f) {
+						push(@new,$f) if $f;
+					} else {
+						my $opath = $action->[3];
+						print STDERR "$revision: $branch: could not fetch '$opath'\n";
+					}
 				}
 			} elsif ($action->[0] eq "D") {
 				push(@old,$path);
 			} elsif ($action->[0] eq "M") {
-				my $f = get_file($revision,$branch,$path);
-				push(@new,$f) if $f;
-			} elsif ($action->[0] eq "R") {
-				# refer to a file/tree in an earlier commit
-				push(@old,$path); # remove any old stuff
-
-				# ... and add any new stuff
-				my($b,$srcpath) = split_path($revision,$action->[1]);
-				$srcpath =~ s#/*$#/#;
-				open my $F,"-|","git-ls-tree","-r","-z", $branches{$b}{$action->[2]}, $srcpath;
-				local $/ = "\0";
-				while(<$F>) {
-					chomp;
-					my($m,$p) = split(/\t/,$_,2);
-					my($mode,$type,$sha1) = split(/ /,$m);
-					next if $type ne "blob";
-					$p = substr($p,length($srcpath)-1);
-					push(@new,[$mode,$sha1,$path.$p]);
+				my $node_kind = node_kind($branch,$path,$revision);
+				if ($node_kind eq $SVN::Node::file) {
+					my $f = get_file($revision,$branch,$path);
+					push(@new,$f) if $f;
 				}
-				close($F);
 			} else {
 				die "$revision: unknown action '".$action->[0]."' for $path\n";
 			}
@@ -554,7 +596,6 @@
 			$pw->close();
 
 			my @par = ();
-			@par = ("-p",$rev) if defined $rev;
 
 			# loose detection of merges
 			# based on the commit msg
@@ -564,11 +605,17 @@
 					if ($mparent eq 'HEAD') { $mparent = $opt_o };
 					if ( -e "$git_dir/refs/heads/$mparent") {
 						$mparent = get_headref($mparent, $git_dir);
-						push @par, '-p', $mparent;
+						push (@parents, $mparent);
 						print OUT "Merge parent branch: $mparent\n" if $opt_v;
 					}
 				}
 			}
+			my %seen_parents = ();
+			my @unique_parents = grep { ! $seen_parents{$_} ++ } @parents;
+			foreach my $bparent (@unique_parents) {
+				push @par, '-p', $bparent;
+				print OUT "Merge parent branch: $bparent\n" if $opt_v;
+			}
 
 			exec("env",
 				"GIT_AUTHOR_NAME=$author_name",
@@ -600,6 +647,10 @@
 		die "Error running git-commit-tree: $?\n" if $?;
 	}
 
+	if (not defined $cid) {
+		$cid = $branches{"/"}{"LAST"};
+	}
+
 	if(not defined $dest) {
 		print "... no known parent\n" if $opt_v;
 	} elsif(not $tag) {
@@ -616,6 +667,7 @@
 		# the tag was 'complex', i.e. did not refer to a "real" revision
 
 		$dest =~ tr/_/\./ if $opt_u;
+		$branch = $dest;
 
 		my $pid = open2($in, $out, 'git-mktag');
 		print $out ("object $cid\n".
@@ -674,13 +726,16 @@
 }
 
 while(++$current_rev <= $svn->{'maxrev'}) {
-	$svn->{'svn'}->get_log("/",$current_rev,$current_rev,$current_rev,1,1,\&_commit_all,"");
-	commit_all();
-	if($opt_l and not --$opt_l) {
-		print STDERR "Stopping, because there is a memory leak (in the SVN library).\n";
-		print STDERR "Please repeat this command; it will continue safely\n";
-		last;
+	if (defined $opt_l) {
+		$opt_l--;
+		if ($opt_l < 0) {
+			last;
+		}
 	}
+	my $pool=SVN::Pool->new;
+	$svn->{'svn'}->get_log("/",$current_rev,$current_rev,1,1,1,\&_commit_all,$pool);
+	$pool->clear;
+	commit_all();
 }
 
 
diff --git a/git-tag.sh b/git-tag.sh
index 6130904..1375945 100755
--- a/git-tag.sh
+++ b/git-tag.sh
@@ -4,7 +4,7 @@
 . git-sh-setup || die "Not a git archive"
 
 usage () {
-    echo >&2 "Usage: git-tag [-a | -s | -u <key-id>] [-f] [-m <msg>] <tagname> [<head>]"
+    echo >&2 "Usage: git-tag [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <tagname> [<head>]"
     exit 1
 }
 
@@ -37,6 +37,13 @@
 	shift
 	username="$1"
 	;;
+    -d)
+    	shift
+	tag_name="$1"
+	rm "$GIT_DIR/refs/tags/$tag_name" && \
+	        echo "Deleted tag $tag_name."
+	exit $?
+	;;
     -*)
         usage
 	;;
diff --git a/http-fetch.c b/http-fetch.c
index ea8af1b..88b74b4 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -569,7 +569,7 @@
 }
 
 #ifdef USE_CURL_MULTI
-void process_curl_messages(void)
+static void process_curl_messages(void)
 {
 	int num_messages;
 	struct active_request_slot *slot;
@@ -625,7 +625,7 @@
 	}
 }
 
-void process_request_queue(void)
+static void process_request_queue(void)
 {
 	struct transfer_request *request = request_queue_head;
 	struct active_request_slot *slot = active_queue_head;
diff --git a/http-push.c b/http-push.c
index 0b90fb9..8866189 100644
--- a/http-push.c
+++ b/http-push.c
@@ -595,7 +595,7 @@
 	}
 }
 
-int refresh_lock(struct active_lock *lock)
+static int refresh_lock(struct active_lock *lock)
 {
 	struct active_request_slot *slot;
 	char *if_header;
@@ -726,7 +726,7 @@
 }
 
 #ifdef USE_CURL_MULTI
-void process_curl_messages(void)
+static void process_curl_messages(void)
 {
 	int num_messages;
 	struct active_request_slot *slot;
@@ -766,7 +766,7 @@
 	}
 }
 
-void process_request_queue(void)
+static void process_request_queue(void)
 {
 	struct transfer_request *request = request_queue_head;
 	struct active_request_slot *slot = active_queue_head;
@@ -799,7 +799,7 @@
 }
 #endif
 
-void process_waiting_requests(void)
+static void process_waiting_requests(void)
 {
 	struct active_request_slot *slot = active_queue_head;
 
@@ -812,7 +812,7 @@
 		}
 }
 
-void add_request(unsigned char *sha1, struct active_lock *lock)
+static void add_request(unsigned char *sha1, struct active_lock *lock)
 {
 	struct transfer_request *request = request_queue_head;
 	struct packed_git *target;
@@ -939,7 +939,7 @@
 	return 0;
 }
 
-static int fetch_indices()
+static int fetch_indices(void)
 {
 	unsigned char sha1[20];
 	char *url;
@@ -1189,7 +1189,7 @@
 	}
 }
 
-struct active_lock *lock_remote(char *file, long timeout)
+static struct active_lock *lock_remote(char *file, long timeout)
 {
 	struct active_request_slot *slot;
 	struct buffer out_buffer;
@@ -1318,7 +1318,7 @@
 	return new_lock;
 }
 
-int unlock_remote(struct active_lock *lock)
+static int unlock_remote(struct active_lock *lock)
 {
 	struct active_request_slot *slot;
 	char *lock_token_header;
@@ -1359,7 +1359,7 @@
 	return rc;
 }
 
-int check_locking()
+static int check_locking(void)
 {
 	struct active_request_slot *slot;
 	struct buffer in_buffer;
@@ -1425,7 +1425,7 @@
 		return 1;
 }
 
-int is_ancestor(unsigned char *sha1, struct commit *commit)
+static int is_ancestor(unsigned char *sha1, struct commit *commit)
 {
 	struct commit_list *parents;
 
@@ -1446,8 +1446,8 @@
 	return 0;
 }
 
-void get_delta(unsigned char *sha1, struct object *obj,
-	       struct active_lock *lock)
+static void get_delta(unsigned char *sha1, struct object *obj,
+		      struct active_lock *lock)
 {
 	struct commit *commit;
 	struct commit_list *parents;
@@ -1503,7 +1503,7 @@
 	}
 }
 
-int update_remote(unsigned char *sha1, struct active_lock *lock)
+static int update_remote(unsigned char *sha1, struct active_lock *lock)
 {
 	struct active_request_slot *slot;
 	char *out_data;
diff --git a/t/Makefile b/t/Makefile
index 5c76aff..5c5a620 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -15,9 +15,14 @@
 
 T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
 
-all:
-	@$(foreach t,$T,echo "*** $t ***"; $(call shellquote,$(SHELL_PATH)) $t $(GIT_TEST_OPTS) || exit; )
-	@rm -fr trash
+all: $(T) clean
+
+$(T):
+	@echo "*** $@ ***"; $(call shellquote,$(SHELL_PATH)) $@ $(GIT_TEST_OPTS)
 
 clean:
 	rm -fr trash
+
+.PHONY: $(T) clean
+.NOPARALLEL:
+
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 4db1bb1..adc5e93 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -25,6 +25,7 @@
 '
 
 . ./test-lib.sh
+TAR=${TAR:-tar}
 
 test_expect_success \
     'populate workdir' \
diff --git a/templates/Makefile b/templates/Makefile
index 07e928e..8f7f4fe 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -13,7 +13,6 @@
 shellquote = '$(call shq,$(1))'
 
 all: boilerplates.made custom
-	find blt
 
 # Put templates that can be copied straight from the source
 # in a file direc--tory--file in the source.  They will be